内存泄漏系列文章:
性能优化——内存泄漏(1)入门篇
性能优化——内存泄漏(2)工具分析篇
性能优化——内存泄漏(3)代码分析篇
一、简述
在上一篇《性能优化——内存泄漏(1)入门篇》中,介绍了内存泄漏的基本概念,并举了一个Demo,结合简单的代码分析,猜测出Demo中存在内存泄漏,并用Android Studio自带的Memory Monitor证明了我们的猜测,但开发中,业务逻辑可能比较复杂,对象引用繁多,难道都要这样去做代码分析吗?肯定不行,程序员的精力有限,且“很懒”(追求效率),我们需要工具来帮助我们进行分析。下面就来看看都有什么神器吧。
二、工具分析
1、System Information
System Information是Android Studio自带的分析工具,可以通过它来判断APP整体是否存在内存泄漏。
一个不存在内存泄漏的APP,在其退出并执行过GC后,APP中所有的View和Activity都会被销毁,所以,我们可以根据退出后内存中View和Activity的数量来判断这个APP是否存在内存泄漏。操作如下:
以上一篇中的Demo为例:
运行APP–>打开一个Activity–>屏幕旋转–>点返回键(两次)直到到桌面–>执行GC–>System Information–>Memory Usage
通过上述操作,会生成一个txt文本,其中就记录着View和Activity的数量。如下图所示,它们的值都为0(主要看Activities),说明本APP不存在内存泄漏。相反的,只要值不为0,那么APP是存在内存泄漏的。
这种方式只能判断整个APP是否存在内存泄漏,但无法知道哪里有内存泄漏,也无法得知是哪些对象引用造成的内存泄漏。
2、Analyzer Tasks
Analyzer Tasks 是Android Studio自带的分析工具,可以帮助我们快速定位内存泄漏,而且使用上 very easy~
打开一个内存快照(.hprof文件),Android Studio的右侧会出现Analyzer Tasks。
打开Analyzer Tasks后,可以发现有2个选项和一个按钮,说明如下图所示,我们需要的就是第一个”Detect Leaked Activities”。
执行任务后,下方的Analysis Results中会得到泄漏的Activity,而且直接定位到了MemoryLeakActivity[0],要上篇中得到的结果一样。
3、MemoryAnalyzer(MAT)
这工具,真心觉得有点复杂,但功能比Android Studio自带的Analyzer Tasks要强大的多。
MemoryAnalyzer是基于eclipse开发的,是一个单独的软件,它也是对.hprof文件进行分析,但必须是标准的.hprof文件,可以从Android Studio中导出。
运行MAT,通过File–>Open Dump分别打开刚刚导出的test1.hprof和test2.hprof,选择Leak Suspects Report–>Finish。
分析的方式有两种,一种是单文件分析,一种是多文件分析,我们先进行单文件分析。
1)单文件分析
切换到test2.hprof,打开Histogram。
点击Histogram后,会出来一个Histogram标签,会列出APP中所有类的实例个数,感觉跟Android Studio的.hprof查看器差不多,MAT中的Objects相当于AS中查看器的Total Count。
通过包名筛选,找出了我们自己写的代码,Android Studio的话可以通过切换视图来定位。可以发现MemoryLeakActivity的实例有2个,说明它可能存在泄漏,再联系之前在获取快照之前执行过GC,而这个Activity还没有销毁,说明这个Activity应该是泄漏了。好,到这里单文件分析就结束了。
2)多文件分析
分别将test1.hprof和test2.hprof的histogram添加到Compare Basket,详细操作如下图所示。
在Compare Basket中会有刚刚添加的两个.hprof,点击红色感叹号进行对比。
出来的Compared Tables列表跟Histogram很像,不过Objects和Shallow Heap标签都是成双成对。
一样,我们只关心我们自己的代码,所以通过Regex进行筛选。通过数据分析,可以知道,在旋转屏幕前后,MemoryLeakActivity的实例由1增加到了2,又因为test2.hprof是在执行了GC后获取的,所以可以判定MemoryLeakActivity在旋转屏幕后,内存泄漏了。
3)引用跟踪
通过单文件分析或多文件分析,我们知道MemoryLeakActivity发生了内存泄漏(被别的实例引用导致无法被GC回收),所以回到test2.hporf的Histogram,筛选出MemoryLeakActivity,右击List objects–>with incoming references。
出来的结果有2大块,即对内存中的2个MemoryLeakActivity实例分别被引用的结果进行了分类,可以看到每个MemoryLeakActivity都被好多个对象引用了。
结合上篇中提到的java中几个特殊类,我们知道SoftReference、WeakReference和PhantomReference在GC一般不会造成内存泄漏,所以这些我们可以不管,也说是说,我们可以对它们进行排除。
对两个Activity分别排除软、弱、虚引用后,得到的结果分别如下:
简单分析下就知道了,第一个MemoryLeakActivity实例是泄漏的,引用它的对象就是CommonUtil。
三、总结
1、工具方面
个人还是比较推荐使用Android Studio集成的System Information和Analyzer Tasks,主要是使用上方便快捷,还很简单,如果你是高手,并且需要有更强大的功能来帮助你检查APP中的内存泄漏的话,建议使用MAT,在网上多找些比较详细的文章看看,本文对MAT的介绍只是冰山一角。
2、操作方面
工具仅仅只是帮助我们快速定位APP中的内存泄漏,并不能直接告诉我们哪里有内存泄漏,实际开发中,需要我们去猜测,去思考,要有清晰的思维,想方设法的构思内存泄漏的判定依据。这需要我们自己去摸索,积累一定的经验。最后,在获取内存快照(Dump Java Heap)之前,建议多点几次Initiate GC,等内存稳定成一条线时再获取内存快照。