通过ADM分析内存泄漏
1.打开ADM,在Android studio中的Tools->Android->Android Device Monitor。出现如下图界面。
2.Devices中选择应用进程,点击Update Heap,然后进行相关操作。
3.在Heap界面中点击Cause GC按钮(回收内存)。
说明:
a) 点击“Cause GC”按钮相当于向虚拟机请求了一次gc操作;
b) 当内存使用信息第一次显示以后,无须再不断的点击“CauseGC”,Heap视图界面会定时刷新,在对应用的不断的操作过程中就可以看到内存使用的变化;
如何才能知道我们的程序是否有内存泄漏的可能性呢。这里需要注意一个值:Heap视图Type叫做dataobject,即数据对象,也就是我们的程序中大量存在的类型的对象。在data object一行中有一列是“Total Size”,其值就是当前进程中所有Java数据对象的内存总量,一般情况下,这个值的大小决定了是否会有内存泄漏。可以这样判断:
a) 不断的操作当前应用,同时注意观察data object的Total Size值;
b) 正常情况下Total Size值都会稳定在一个有限的范围内,也就是说由于程序中的的代码良好,没有造成对象不被垃圾回收的情况,所以说虽然我们不断的操作会不断的生成很多对象,而在虚拟机不断的进行GC的过程中,这些对象都被回收了,内存占用量会会落到一个稳定的水平;
c) 反之如果代码中存在没有释放对象引用的情况,则dataobject的Total Size值在每次GC后不会有明显的回落,随着操作次数的增多Total Size的值会越来越大,直到到达一个上限后导致进程OOM被kill掉。
内存分析工具 MAT(Memory Analyzer Tool)
并不是所有的内存泄漏都可以用观察heap size的方法检测出来,因为有的程序只是泄漏了几个对象,而且泄漏的对象个数不会随着程序的运行而增加,这种内存泄漏不会直接导致OOM,但是无用对象无法回收,无疑是对内存的浪费,会影响到程序的性能,我们需要使用MAT工具才能发现这种比较隐蔽的内存泄漏。
使用MAT之前有2个概念是要掌握的:Shallow heap和Retained heap。Shallow heap表示对象本身所占内存大小,一个内存大小100bytes的对象Shallow heap就是100bytes。Retained heap表示通过回收这一个对象总共能回收的内存,比方说一个100bytes的对象直接或者间接地持有了另外3个100bytes的对象引用,回收这个对象的时候如果另外3个对象没有其他引用也能被回收掉的时候,Retained heap就是400bytes。
使用过程:
1.下载MAT
2.点击Update Heap,然后进行应用操作,最后点击Dump HPROF file,导入文件:包名.hprof。
3.打开mat文件中的MemoryAnalyzer,导入之前的hprof文件。理论上会出现如下图的问题
4.这是因为MAT是用来分析Java程序的hprof文件的 与Android导出的hprof有一定的格式区别,因此我们需要把导出的hprof文件转换一下,sdk中提供给我们转换的工具 hprof-conv.exe 。hprof-conv.exe的位置一般在sdk->platform-tools下
图3.png接下来我们cd到这个路径下执行这个命令转换我们的hprof文件即可:hprof-conv 导入文件位置 导出文件位置
图4.png5.导入转换后的hprof文件,得到如下图所示界面
图5.png利用mat分析内存泄漏
再看下方Action中有Dominator Tree和Histogram的选项(图5中有标注),这一般来说是最有用的工具。
点开Dominator Tree,会看到以Retained heap排序的一系列对象。
图6.png找到可能是由于内存泄漏造成的。所以我们右键点击这行,选择Path To GC Roots ->exclude weak references,可以看到下图的情形:
图7.png点开Histogram,会看到以Shallow Heap排序的一系列对象。
图8.png右键选中byte[]数组,选择List Objects -> with incoming references,可以看到byte[]具体的对象列表:
图9.png找到可能是由于内存泄漏造成的。所以我们右键点击这行,选择Path To GC Roots ->exclude weak references,显示如图6。
点开OQL,输入SQL语句寻找对象。
我们就可以OQL查看当前内存中存在的对象了,由于我们内存泄漏一般发生在Activity中,因此只需要查找Activity即可。
图10.png有5个MemoryLeakActivity,怀疑存在内存泄漏问题,选择一个Activity右键->Path To GC Roots ->exclude weak references,得到如下图所示:
图11.png看到 this$0引用了这个Activity,而this$0是表示内部类的意思,也就是一个内部类引用了Activity ,而 this$0又被target引用,target是一个线程。原因找到了,内存泄漏的原因就是Activity被内部类引用,而内部类又被线程使用因此无法释放。
网友评论