内存泄漏探讨下(解决方法)

    |     2016年3月3日   |   Android经验   |     0 条评论   |    1527

发生内存泄漏,先查再解。检查用 DDMS 里的 Heap,分析用 MAT;日常开发还可以接 LeakCanary,在 debug 包里把 dump 和路径算好。


一、内存泄漏的检查工具 Heap

工欲善其事必先利其器。Heap 用来大致判断有没有泄漏,MAT 用来分析泄漏发生在哪里。开发环境 DDMS 能找到这两个。

201601120923191

具体操作:

  1. 在 Devices 设备列表里找到设备,点要监控的进程。
  2. 点 “Update Heap” 更新堆内存。
  3. 点 “Heap” 视图看内存。
  4. 每次 Activity 退出和进入时点 “Cause GC”,手动调用 GC。
  5. 观察 data object 那一行。每次点 Cause GC 看 Total Size,如果不断增大,说明存在泄漏。

先模拟泄漏,再用 Heap 判断。泄漏代码如下:

public class LeakAty extends Activity {

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

    /**
     * 测试内存泄漏的代码
     */
    private void testLeak() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }
}

new Runnable(){} 是非静态匿名内部类,会强引用外围对象 LeakAty。打开方向旋转,不断转屏让 LeakAty 反复创建。没有这段代码时,旧 Activity 会在 onDestroy 之后被回收;有了它,Runnable 一直跑、不会被回收,它强引用的 LeakAty 也不会被回收。转 n 次,内存里就有 n+1 个 LeakAty。

Heap 第一次按 Cause GC:

201601120923202

上图 data object 的 Total Size 为 1.031M。多次旋转后再看:

201601120923203

Total Size 变成 2.059M。每次 GC 后 data object 总大小没有回落,可以证实存在泄漏。泄漏发生在哪,交给 MAT。


二、内存泄漏的分析工具 MAT

MAT 需要 .hprof。通过 “Dump HPROF file” 转存当前堆,保存为 1.hprof。

201601120923204

导出的 1.hprof 要用 ..\sdk\tools\ 下的 hprof-conv.exe 转换,才能被 MAT 导入,转成 out1.hprof。

201601120923205

File → Open Heap Dump… 导入 out1.hprof。

201601120923206

点左边 Overview,Actions → Histogram。

201601120923207

想知道 Activity 有没有泄漏,输入关键词 Activity,回车。

201601120923208

下图搜索结果里 Activity 实例有 7 个。点选中红色框,右键 → Merge Shortest Paths to GC Roots → exclude all phantom/weak/soft etc. references。排除虚引用、弱引用、软引用,剩下都是强引用。

201601120923209

过滤出来的强引用列表里,这七个实例都被 Thread 引用,证实上面的代码确实泄漏。

2016011209232010


三、使用 LeakCanary 检测 Android 的内存泄漏

什么是 LeakCanary?为什么选它?

LeakCanary 是 Pierre-Yves Ricau 开发的开源库,用来检测内存泄露。对战泄漏通常要经过:

  1. 了解 OutOfMemoryError 情况。
  2. 重现问题。
  3. 发生泄露时把内存 Dump 出来。
  4. 分析 dump,定位可疑对象。
  5. 计算这个对象到 GC roots 的最短强引用路径。
  6. 确定路径里哪个引用不该有,然后修复。

很复杂。如果有一个库能在 OOM 之前把这些事情做完,你只要修问题就好——LeakCanary 做的就是这件事。可以在 debug 包里轻松检测内存泄露。

工具 干什么 关键动作
Heap 判断有没有泄漏 Cause GC 后看 data object Total Size 是否只增不减
hprof-conv + MAT 定位谁握着对象 Histogram → Merge Shortest Paths to GC Roots(排除弱/软/虚引用)
LeakCanary debug 包自动走完上述步骤 你负责修那条不该存在的强引用

一句话总结:Heap 看 Total Size 只增不减,MAT 沿强引用找到 GC Root,LeakCanary 则把 dump 和路径计算提前做完,你只负责剪断那条不该有的边。

转载请注明来源:内存泄漏探讨下(解决方法)
本文链接地址:https://ai.zhousir.top/?p=1361
回复 取消