内存泄露测试的整个过程如下:
在手机里启动被测APP并打开DDMS。
在DDMS中选中【com.example.android.hcgallery】之后单击按钮【show heap updates】,然后切换到标签页【VM Heap】,再单击按钮【Cause GC】。
不断操作APP,并观察Heap。经过一段时间的操作我们发现不论是%Used还是data object的Total Size都在不断增加,如图。正常情况下Total Size会稳定在一定范围内。
图 VM Heap 1
即使进行Cause GC之后仍会继续增加,如图
图 VM Heap 2
此时我们怀疑如果长期下去可能有内存泄露的可能性,为了进一步分析我们单击按钮【Dump HPROF File】,得到一个后缀为hprof的文件(生成该文件的时间较长,请耐心等待)。
使用命令hprof-conv将得到的hprof文件转化为标准的hprof,这样MAT才能识别。
使用MAT打开转化之后的hprof文件,选择图中的【Leak Suspects Report】即可。之后你会看到一个概要信息,如图。它只是给出一个宏观的概念,告诉你某些问题的占比,对于分析并没有实质性的帮助。
单击柱形图标按钮【Histogram】会生成一个视图,它显示的是类实例的列表,其中Shallow Heap代表对象自身占用的内存大小,不包括它引用的对象。Retained Heap代表当前对象大小和当前对象可直接或间接引用到的对象的大小总和。
在Shallow Heap列进行从大到小的排序,我们发现byte[]占比最大,选中之后右键依次选择【List objects】>【with incoming referenes】进行钻取,如图。
图 Histogram视图
再次按照Shallow Heap列进行从大到小的排序,选择一个较大的对象并依次展开它的路径,如图。这里我们关注自己写的代码,也就是com.example.android.hcgallery.ContentFragment,我们发现mBitmap比较可疑。这种方法适合对代码比较熟悉的朋友。
图 with incoming references
除了上述方法之外你也可以通过排除弱引用来分析,这样能减少干扰因素。先按照Retained Heap从大到小排序,之后右键最大的,依次选择【Path To GC Roots】>【exclude weak references】,得到如图的数据。发现sBitmapCache这个变量占的比例较大,可能有问题。
图 exclude weak references
小强课堂 在Java中存在强引用、弱引用、软引用,这里给大家做一个普及:
强引用:垃圾回收不会回收它,需要我们主动设置为null。
弱引用:顾名思义比较弱,当垃圾回收发现它只具有弱引用对象时,不管当前内存空间是什么情况,都会进行回收。
软引用:如果它只具有软引用对象时,内存空间足够,垃圾回收器就不会回收。但如果内存空间不足了,就会回收。
之后排查代码发现sBitmapCache是static HashMap,mBitmap使用完成之后没有recycle掉。
-
最后修改代码,在mBitmap != null时进行mBitmap.recycle()。
这样一步步做下来至少你不会觉得混乱,而是有规可循。另外,有些内存泄露的分析可能没法一次性分析出来,需要多次,这就考验大家的耐心了。其实只要记住,分析要有规可循,耐心必不可少,任何难题都可以解决的。