购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

2.1.1 手动分析

手动分析内存泄漏时,首先需要我们通过命令抓取Hprof(Heap profile,内存快照)文件,该文件是虚拟机的一种堆转储文件,记录了程序在运行过程中的堆内存分配和对象使用情况,用于分析Java应用程序的内存使用情况;然后通过Hprof文件分析对象的引用链,以此来发现和解决内存泄漏问题。

通过“adb shell am dumpheap<process_id><output_file>”命令即可抓取程序的Hprof文件,本书示例程序中的Hprof文件的抓取代码如下。

抓取到Hprof文件后,通过pull命令将文件从手机目录拉取到本地计算机目录,之后就可以进行分析了。我们有两种方式来进行分析,一种是直接通过Android Studio自带的Profile工具来分析,另一种是通过MAT工具来分析。

1.Android Studio分析

打开Android Studio的Profile工具,直接将刚抓取的Hprof文件导入进去即可进行分析,如图2-2所示,单击Leaks,就能看到JavaLeakActivity发生了泄漏。

图2-2 Android Studio分析Hprof的界面

单击References,我们就能看到JavaLeakActivity的引用链,从而发现该Activity被JavaLeakActivity$1这个内部类持有并发生了内存泄漏,右键单击Jump to Source就可以直接跳转到源码的路径中。在分析界面中,我们能看到泄漏的Activity出现了Shallow Size(浅堆大小)和Retained Size(保留堆大小),这两个“Size”的解释如下。

❑Shallow Size:该对象本身占用的内存大小,不包含其所引用的对象占用的内存,可以看到JavaLeakActivity对象自身占用的内存大小只有328B。

❑Retained Size:该对象本身占用的内存大小,加上能从该对象直接或间接访问的对象的内存大小。如果该对象发生了内存泄漏,保留堆的大小便是内存泄漏的大小。

通过保留堆大小,我们就能知道JavaLeakActivity产生了109 460B的内存泄漏。

2.MAT分析

除了使用Android Studio,我们还可以使用Eclipse的MAT工具来分析Hprof文件。在Eclipse官网便能下载该工具。通过dumpheap命令抓取的Hprof文件是无法直接在MAT中使用的,我们需要使用Android SDK中platform-tools目录下的hprof-conv工具进行转换。转换命令如下。

我们将转换后的after_demo.hprof拖入MAT工具中,便会看到如图2-3所示的界面。

图2-3 MAT分析hprof的界面

对界面中的主要功能解释如下:

❑Histogram:显示各个对象的实例数量和浅堆、保留堆的大小,可以按照不同的排序方式和分组方式查看各个对象的信息,或者使用过滤器来筛选出感兴趣的对象。

❑Dominator Tree:显示各个对象的浅堆、保留堆的大小及它们的引用关系。

❑Top Consumers:显示堆中占用内存最多的对象。

❑Leak Suspects:显示可能存在内存泄漏的对象。MAT会对堆内存中的对象进行遍历,找出GC Root持有的所有对象,并根据对象的保留堆大小、引用路径、类别等信息来分析对象是否出现内存泄漏,然后按照保留堆的大小从大到小排序,选出前几个作为内存泄漏的嫌疑对象。需要注意的是,这里的结果只是可能泄漏,并不代表真正出现内存泄漏,我们还需要对结果进行进一步的判断和分析。

单击Leak Suspects,进入如图2-4所示的界面,可以看到JavaLeakActivity并没有出现在内存泄漏的列表中,这主要是因为MAT和Android Studio在内存泄漏检测机制方面的差异而导致的。MAT是针对Java的通用内存分析工具,因此并不会对Activity进行特殊的泄漏判断,而Android Studio会针对Activity、Fragment等对象进行特殊的泄漏逻辑判断,即onDestory执行后,Activity、Fragment等对象未被回收即表示发生了内存泄漏。

图2-4 MAT检测泄漏界面

既然在Leak Suspects里没有发现有用的信息,那MAT要如何分析内存泄漏呢?实际上,MAT最主要的作用是在我们已经知道某个对象发生泄漏的情况下,分析对象的引用链等更详细的信息,从而帮助我们解决问题。通过单击Dominator Tree,在顶部输入JavaLeakActivity,即可显示该对象的引用链,如图2-5所示。

图2-5 JavaLeakActivity的引用链

此时界面中出现的引用链包含了所有的Incoming references(入向引用)和Outgoing references(出向引用),所以条目比较多,很难进行分析。我们需要用右键单击List objects,选择with incoming references,即选择入向引用,如图2-6所示。

图2-6 选择入向引用

这里笔者需要解释一下什么是Incoming references(入向引用)和Outgoing references(出向引用)。如图2-7所示,对象C的入向引用是对象A、对象B,对象C的出向引用是对象D、对象E。因此当其他对象持有对该对象的引用时,这些对象被称为入向引用对象,当一个对象持有对其他对象的引用时,这些对象被认为是出向引用对象。通过入向引用,我们就能知道JavaLeakActivity被哪些对象持有,从而去切断引用链来修复内存泄漏。

图2-7 对象引用关系

进行Incoming references筛选后,可以看到所有引用了JavaLeakActivity的对象,这里我们还要继续筛选,单击Path To GC Roots,并选择exclude all phantom/weak/soft etc.references来排除所有不会导致内存泄漏的软引用、弱引用等引用链,如图2-8所示。

图2-8 排除引用链

此时我们就能看到JavaLeakActivity被JavaLeakActivity$1这个内部类持有并发生了内存泄漏,如图2-9所示,分析的结果和Android Studio中的结果是一致的。

图2-9 导致内存泄漏的引用链

MAT相比于Android Studio,功能更强大,性能也更好,更适用于复杂的场景,但是使用起来比较复杂。而Android Studio使用起来更简单,对于一些引用链路径短、引用关系简单等不太复杂的场景,使用Android Studio来分析即可。 qeMhvtSCDAtja28pQRqr9EQwkoeTWh1LKkIBiRYHMaXFewis7nJMS+hY+OTQIkyn

点击中间区域
呼出菜单
上一章
目录
下一章
×