内存泄露实例分析 -- Android内存优化第四弹

cover

引言

前文内存分析工具集中介绍了一系列的内存分析工具及其基本使用, 诸如Memory Monitor, HPROF Viewer, MAT等等. 实际上了解了工具的使用, 我们就已经掌握了如何分析内存问题了.

为了能对工具的使用更加深入, 本篇将一个代码片段为例, 从时序的角度讲解下如何使用这些工具来分析一个内存泄露.

系列文:
1.GC那些事儿
2.Android的内存管理
3.内存分析工具
4.内存泄露实例分析

1, 例子

假设有一个单例的ListenerManager, 可以add / remove Listener, 有一个Activity, 实现了该listener, 且这个Activity中持有大对象BigObject, BigObject中包含一个大的字符串数组和一个Bitmap List.

代码片段如下:

ListenerManager

public class ListenerManager {

    private static ListenerManager sInstance;
    private ListenerManager() {}

    private List<SampleListener> listeners = new ArrayList<>();

    public static ListenerManager getInstance() {
        if (sInstance == null) {
            sInstance = new ListenerManager();
        }

        return sInstance;
    }

    public void addListener(SampleListener listener) {
        listeners.add(listener);
    }

    public void removeListener(SampleListener listener) {
        listeners.remove(listener);
    }
}

MemoryLeakActivity

public class MemoryLeakActivity extends AppCompatActivity implements SampleListener {

    private BigObject mBigObject = new BigObject();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_memory_leak);

        ListenerManager.getInstance().addListener(this);
    }

    @Override
    public void doSomething() {

    }
}

具体代码参看Github.

2, 使用Android Studio的自带工具来分析

根据前文的工具介绍, Android Studio自带了Memory Monitor, HPROF Viewer & Analyzer来分析内存使用及内存问题.

2.1 查看Memory使用, 并导出hprof文件

启动我们要检测的Activity(MemoryLeakActivity), 然后退出, 在monitor中查看内存变化:


2.2 在HPROF Viewer界面, 开始分析

第一步

点击"Analyzer Tasks"视图中的启动按钮, 启动分析

第二步

查看"Analysis Result"中的分析结果, 点击"Leaked Activityes"中的具体实例, 该实例的引用关系将会展示在"Reference Tree"视图中.

第三步

根据"Reference Tree"视图中的引用关系找到是谁让这个leak的activity活着的, 也就是谁Dominate这个activity对象.

此例中, 比较简单, 可以很清晰看到是ListenerManager的静态单例sInstance最终支配了MemoryLeakActivity. sIntance连接到GC Roots, 故而导致MemoryLeakActivity GC Roots可达, 无法被回收.

关于Dominate, GC Roots, GC Roots可达, 活对象等概念, 请结合前两篇的理论文1, 2观看.

2.3 使用Heap Viewer查看内存消耗

上述步骤, 可以让我们快速定位可能的内存泄露. 当然, 内存问题, 除了内存泄露, 还有内存消耗过大. 我们可以在Heap Viewer中查看分析内存的消耗点, 如下:

3, MAT让我们看的更多

就单纯的分析Android App的内存使用和内存泄露来说, 个人觉得Android Studio自带的工具已经足够好了, 而且再持续变得更好, 也更便于Android的开发人员去理解. 故而其实一开始在Android性能分析工具一文中, 我就没有详细去提MAT. 相对与Android Studio中的Memory Monitor, HPROF工具来说, MAT的使用显得更加生涩, 难以理解.

关于MAT的帮助文档, 个人翻译了一份, 需要的同学戳这里.

当然, 如果我们想对内存的使用相关知识了解得更多, 还是有必要了解下MAT的...
下面我们以几个角度来了解下MAT的基本使用:

再次:
Android Studio导出的hprof文件需要转换下才可以在MAT中使用.

$ hprof-conv com.anly.samples_2016.10.31_15.07.hprof mat.hprof

3.1 Histogram视图定位内存消耗

MAT中很多视图的第一行, 都可以输入正则, 来匹配我们关注的对象实例.

3.2 Dominate Tree视图查看支配关系

3.3 使用OQL查询相关对象

对于Android App开发来说, 大部分的内存问题都跟四大组件, 尤其是Activity相关, 故而我们会想查出所有Activity实例的内存占用情况, 可以使用OQL来查询:


具体OQL语法看这里.

3.4 GC路径定位问题

上面几个视图都可以让我们很快速的找到内存的消耗点, 接下来我们要分析的就是为何这些个大对象没有被回收.

根据第一弹:GC那些事儿所言, 对象没有被回收是因为他有到GC Roots的可达路径. 那么我们就来分析下这条路径(Path to GC Roots), 看看是谁在这条路中"搭桥".

如下, 进入该对象的"path2gc"视图:
![Slice 1](http://oat9lzupi.bkt.clouddn.com/Slice 1.jpg)

同样, 与HPROF Analyzer异曲同工, 找出了是ListenerManager的静态实例导致了MemoryLeakActivity无法回收.

4, LeakCanary让内存泄露无处可藏

大道至简, 程序员都应该"懒", 故而我们都希望有更方便快捷的方式让我们发现内存泄露. 伟大的square发挥了这一优良传统, LeakCanary面世.

4.1 加入LeakCanary

app的build.gradle中加入:

dependencies {
   debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5'
   releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
   testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
 }

Application中加入:

public class SampleApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();

        LeakCanary.install(this);
    }
}

4.2 操作要检测的界面, 查看结果

当发生可疑内存泄露时, 会在桌面生成一个"Leaks"的图标, 点击进去可以看到内存泄露的疑点报告:


可以看到, 结果与前二者的分析结果"惊人"一致, 不一致就出事儿了, :)

足够方便且直观吧, 赶快用起来吧.
当然, 内存问题不仅仅是内存泄露, 还有内存占用过多等, 这时我们就需要借助前两种工具了.

结语

综上, 建议LeakCanary集成作为App的必选项, 大多数情况下我们可以用LeakCanary结合Android Studio自带的工具分析内存问题.

如果有精力, 还是建议深入了解下MAT, 能让我们更深入了解GC的机制, 相关概念, MAT还有很多更高端的功能值得我们探索, 例如Heap比较等.

其实, 内存问题的分析, 无外乎分析对象的内存占用(Retained Size), 找出Retained Size大的对象, 找到其直接支配(Immediate Dominator), 跟踪其GC可达路径(Path to GC Roots), 从而找到是谁让这个大对象活着. 找到问题症结, 对症下药.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,565评论 25 707
  • 内存管理的目的就是让我们在开发中怎么有效的避免我们的应用出现内存泄漏的问题。内存泄漏大家都不陌生了,简单粗俗的讲,...
    宇宙只有巴掌大阅读 2,329评论 0 12
  • 四月的计划完成的如何了?五月的计划里,四月的计划是否重复着? 四月的你,日子过得好么?五月的你,准备怎样活? 上个...
    MsCactus阅读 238评论 0 2
  • Q:小丸子张晗你好!听说你是在职事业单位工作,同时业余时间兼职做微商,平时还要带十个月大的宝宝,请问你为什么要参加...
    奔跑的丸子啊阅读 450评论 9 52
  • 独立欢腾舒骏马,野乡纵横自乾坤。 平生不做安宁事,初老犹痴寨里春。 千里奋蹄戏千里,风云变换淡风云。 世间俗事逍遥...
    溪竹青阅读 627评论 0 7