解决Android内存泄漏--MAT\android studio使用方法实例

前言

  • 作为一名android客户端开发人员,肯定会遇到过ANR或者OOM的案例, 少部分案例除了部分业务上面的代码同步或者死锁的原因导致的外,绝大多数都是因为我们缺乏良好的代码质量导致内存泄漏或者说app内存优化的经验.我想在实际上庞大的android项目里从java代码层面解决掉内存泄漏是令每一个初级android开发人员头疼的事.
  • 是否你对测试提过来的ANR或者OOM的大篇log表示束手无策呢~ 笔者也是过来人
  • 这里笔者就结合自身成长经历和实际操作分享一下.

造成内存泄漏的原因

一般情况,结合这一年项目的开发经验,我总结有以下多数两种情况造成的.
ps : 相信还有其他造成泄漏的因素,可自行google,这里笔者就结合自己的开发经验和体会来说了.´ ▽ ` )ノ

  • bitmap,cursor等使用后没有及时释放对应资源,造成内存泄漏,尤其是自定义控件.或者项目Gif图播放的使用.(这里相信很少人会发现这个问题,相信绝大多数开发者在gif的使用上,会使用glide的那套gif播放方案.那你就Too young了,仔细看glide关于播放管理Gif的代码,你就会发现,gif在关闭时,内存并没有释放,并且在进程中占用了大量的内存,这里笔者想单独开篇来说明这个问题,这里暂时抛砖引玉暂时不详细说了吧)
  • 异步请求的callback持有的强引用不能被回收导致内存泄漏.这里最明显的案例当属: AsyncTask了吧.只要异步回调持有context 或者 activity , 结果可想而知了吧

使用android studio排查java代码

主要是2个点 :

  • 在项目里引用大名鼎鼎的square公司的leakcanary . github : https://github.com/square/leakcanary. 但是有个缺点 : 对于Bitmap回收或app的内存管理,在leakcanary里面是体现不出来的, 他只能给你提出代码里不能不回收的强引用引用链,方便开发者追根溯源.不过这也很给力了.

  • 其次使用dumpsys meminfo命令+android studio的memory monitor找到不能不回收的activity,在配合MAT来找到具体的不能被回收的引用链.

    1. dumpsys meminfo {$PackageName} ;如下 :
      请注意看下面的activities的堆栈 , 有3个. 明明我们现在手机上app里面的堆栈只有1个activity,怎么会有3个?这里就表面有2个activity的object被hold住了,不能及时释放.但我们现在不知道是哪个activity泄漏了,没关系,下面我们来使用android studio找出具体的activity.
      这里写图片描述

    2. 使用android studio的memory monitor + Analyzer , 如下 :
      这里写图片描述

    3. 生成hprof文件后,使用android studio自带的Analyzer分析出发生泄漏的activity.如下 :
      这里写图片描述

    4. 观察Reference Tree,可能有人会说里面hold的对象这么多,我怎么知道是哪个对象的持有造成了不能被回收? 这里android studio自带的Analyzer相对于MAT的劣势就表现出来了,不过没关系,他帮我们分析出Leaked Activities就可以了.接下来就是配合我们的MAT找到具体的不能被回收的原因. 请记住这里泄漏的activity名称

使用MAT定位具体泄漏原因

网上有很多关于MAT的使用教程,我觉得有些是时间原因,太久远了,新手上手并不是很容易,并且如果自己没有正确使用过一次MAT找出泄漏根源,你真的很难上手.

  1. 你先要把刚刚生成的hprof文件生成标准的hprof文件,在使用mat打开它.如图 :
    这里写图片描述

  2. 打开刚刚生成的标准hprof,这里请直接关注Histogram,关于打开生成的那些报表啥的,可以不用关注,对于你找出泄漏根源没什么实际上的用处.如图 :
    这里写图片描述

  3. 大家可以看到Regex这个关键词了吧,没错.我们现在要干的就是把刚刚通过android studio分析出来的泄漏的activity名称作为正则表达式关键字进行搜索. 如果没有内存泄漏,按么Object这一栏应该是为0,但现在出现了1,这就表明出现了内存泄漏,强引用代码不能被回收 如图 :
    这里写图片描述

  4. 好,接下来,就仅需要通过最后两步就能找出泄漏的真正根源了.

    (1) 这里右键选择list objects,这里弹出来with incoming reference 和 with outgoing reference这两个,这里解释一下,incoming表面内部持有的对象,outgoing表面当前对象被哪些对象作为内部对象持有的.因此我们这里应该选择with incoming reference

    (2)同样这里邮件path to GC roots,同gradle dependencies的 exclude一样,这里exclude掉weak/soft reference,最终弹出来的表格,你就会发现很难在庞大项目里面找到的,java代码深处被泄漏的代码了

  5. 最后,发现原来始作俑者是android原生的AccountManager.AmsTask.
    这里写图片描述

  6. 是不是发现android源码类似AsyncTask , AccountManager.AmsTask很坑爹了. 这就是为什么Rxjava一出来,这么受欢迎,普及如此之广的原因了.对于常规的Async异步等处理,Rx的使用比我们之前的java封装好太多了,大家可以回想一下之前项目在未使用Rx的时候,是如何封装一些异步请求的, 是不是花了很大的篇幅很多的类来封装一些async基类,但如果稍不留神就会写出内存泄漏的代码?

  7. 有兴趣的童鞋可以看看16年JakeWharton关于Rxjava的演讲视频https://www.youtube.com/watch?v=htIXKI5gOQU. 也很明确的指出了rx之前通常异步的处理方式存在的问题

最后

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值