美文网首页性能优化
Android内存泄漏实战-MAT

Android内存泄漏实战-MAT

作者: d06ab7d63a9a | 来源:发表于2017-07-15 22:07 被阅读66次

本文为博主原创文章,转载请注明出处。
本文是实际项目中的真实案例,部分截图中马赛克了包名信息

起因

日常coding的过程中,一般很少会特意的去做内存泄漏的检查。项目上线后,出现了一个bug,经过调查发现,某个Activity中的成员变量被重置成了初始值,检查完所有代码后得出结论,是由于某种情况下的内存回收导致的重置。但内存回收的原因一直没有查明,之前一直认为是低配置的手机的问题,直到高配的手机也出现这个问题后,我考虑到可能是由于内存泄漏引起的Activity回收。

工具选择

有以下三种工具供我们选择,排查代码中出现的内存泄漏问题。

  • MAT :Memory Analyzer Tool,一个基于Eclipse的内存分析工具,是一个快速、功能丰富的JAVA heap分析工具。有独立使用的版本(下载地址)。
  • Android Studio :Android Studio 1.1及更高的版本,内置了内存泄漏检测的工具。
  • LeakCanary :Square开源的内存泄漏检查库,可以直接集成到代码中。(Github地址

如果不知道那个页面出现内存泄漏,可以直接集成LeakCanary,将App跑一遍,LeakCanary会自动通知你哪里出现了内存泄漏,非常方便。如果确定了某个页面出现问题,想详细查询内存泄漏的相关信息的话,还是建议使用MAT工具来分析,MAT能给出非常全面且直观的数据。
最终,我决定使用Android Studio和MAT来分析内存泄漏问题。

使用Android Studio导出MAT所能识别的hprof文件

  • 打开Android Monitor中的Monitors标签,我们可以实时监控到App的Memory、CPU、NetWork、GPU使用状态。
  • 打开App中出现内存泄漏问题的页面(比如我的项目中是KnowledgePointActivity),退出该页面,然后在Memory中点击“Initiate GC”,手动执行GC操作。

提示:这里的手动GC操作不能省,否则会导致有错误数据误导你,后面会有实例展示这个“坑”。

  • 反复执行上述操作几遍。
  • 点击“Dump Java Heap”,Android Studio(下称AS)会自动生成hprof文件,并自带的内存泄漏分析。顺便讲一下,AS自带的内存泄漏分析也非常方便,能自动分析哪个页面出现内存泄漏问题。只需要点击“Analyzer Tasks”页签中的“Perform Analysis”按钮即可。
  • AS自动生成的hprof并不能被MAT识别,需要做一次转换。在“Captures”页签中可以找到我们生成的hprof文件,右键该文件,点击“Export to standard .hprof”,就可以生成MAT能识别的文件了。

使用MAT分析内存泄漏

第一次打开hprof文件会有一些初始化操作,只要按照默认配置下一步即可。
生成报告后,打开OverView界面,并点击“Histogram”(列出内存中的对象,对象的个数以及大小),从这些对象中寻找我们想找到的内存泄漏路径。
有如下两种方法进行定位:

方法一:使用OQL执行语句查询

  • 点击“OQL”图标。
  • 在窗口输入select * from instanceof android.app.Activity,然后点击“!”按钮。

方法二:Histgram中进行逐步筛选

  • 打开Histgram页面,第一行我们可以通过正则表达式来查到我们想定位的类(如输入“Activity”,则可以筛选出所有的Activity)
  • 找到我们的指定页面(本文中是KnowledgePointActivity),右键选择List objects → with incoming reference

通过上述两种方法,我们就能找到该页面下所有没有被回收的对象。如下图所示:

选择其中任意一条记录,右键选择Path to GC Roots → exclude weak/soft references,新页面中展示的是列出所有强引用的GC Roots结点,这里就能很直观的发现是哪里强引用了Activity对象,导致无法回收。

如下图所示,mBtnList对象强持有了Activity的引用。


回归代码中发现问题

那么上面我们找到的mBtnList具体为什么会强持有Activity的引用呢?
检查代码后发现,该对象的相关代码如下:

private static ArrayList<HashMap<String, Object>> mBtnList;

HashMap<String, Object> map = new HashMap<>();
map.put("type", "string");
map.put("view", (View) child);
mBtnList.add(map);

我这里错误的将持有Activity引用的View对象加入了一个static的对象中,这样就直接导致了在Activity销毁时无法完成对该对象的回收。

这里将mBtnList改为非静态的对象即可解决问题,修改完后再重新执行上述步骤,检查内存泄漏,发现问题已经成功解决了。

提示:静态有风险,使用需谨慎。

内存泄漏检查过程中可能出现的“坑”

  • 在生成hprof文件时,如果没有手动执行GC操作,会产生一些“错误”的数据误导你,如下图:

理论上,运行10遍Activity,如果不执行GC操作,就会有10个上图中的对象出现,这些对象(虚引用的对象?)在GC时会被销毁,不属于内存泄漏的范畴。

相关文章

网友评论

    本文标题:Android内存泄漏实战-MAT

    本文链接:https://www.haomeiwen.com/subject/tzvzhxtx.html