Android内存泄漏 -- Terminal + Mat

Android 的内存泄漏问题是在日常开发中经常遇到的问题,Android Studio 也提供了Android Profile 来分析App性能和内存情况。但是Android Profile 打开后,经常会导致编译器卡顿。为了快捷方便的分析App的内存问题,我通过简单的 terminal 命令来导出heap数据,通过mat工具来分析查看heap信息。

分析内存泄漏,就是分析在进程的堆内存中存在那些不应该存在的对象,也就是那些对象是已经不需要存在的,但是由于一些不合理的引用或者bug,导致这些对象依然被引用,从而对象无法被回收,持续占用内存。介于此,我们要做的就是两件事,第一步,通过命令导出heap,第二步分析heap里面存在的对象。

工具介绍

Terminal:命令行终端。
Mat:Memory Analyze Tool,内存分析工具,Eclipse时代DDMS自动集成了。现在有独立应用可以下载。

导出Heap

Heap就是Android App 运行时的堆内存信息。通过Heap,我们可以清晰的看到现在内存中还有那些对象留存。

触发GC

Java虚拟机有自己的内存回收机制,无论是哪种内存回收策略,内存回收都不会是实时进行的,也就是,并不是一个对象在左右一个引用释放之后,就立刻会被回收的。GC的时机是虚拟机自动执行,通常在内存不足时会触发GC。也就是如果我们在直接导出heap,不触发GC,很可能会看到很多已经可以被回收但是未被回收的对象。为了避免这些对象影响我们的结果分析,我们需要在导出Heap之前,手动触发一次GC。

如果在Android代码里面触发GC,我们直接使用System.gc()或者是Runtime.getRuntime().gc来处理,但是为了不侵入代码,我们希望可以通过adb 命令解决问题。在网上找到了手机GC 的 源码,其实是向对应进程发送两个kill 命令"kill -10 " + pid。按照这种思路,我尝试了一下。
在这里插入图片描述
首先通过ps 命令打印出当前手机的进程信息,一般会有很多数据,为了便于查看,我们过滤一下,grep packageName 可以打印出对应包名的进程信息。打印的结果中,第二列为pid,左右一列为包名。取得pid之后,执行kill 命令,很遗憾,Terminal告诉我们Operation not permitted。

这时候,我想到 adb shell dumpsys meminfo 命令可以打印出来当前app 的内存数据,这个命令为了保证打印出的数据比较准确,应该会触发一次GC的。尝试了一下,然后在LogCat里面看到触发GC 的 Log。

导出heap信息

使用 adb shell am dumpheap 命令导出heap信息到手机。
在这里插入图片描述可以看到,生成了一个heapdump-20200404-153327.prof文件,放在手机目录的 /data/local/tmp/ 文件夹下,然后通过adb pull 命令,将文件从手机上拉到电脑上面。到这里,我们成功把heap文件导出来了。

Heap分析

hprof-conv

打开我们之前安装的Mat,点击 File -> Open File 选中我们刚才拉到电脑上面的文件。这时你会发现,Mat会弹出一个错误提示,说文件格式有问题。因为Mat 接受的是hprof文件,而我们导出来的prof文件。通过hprof-conv命令可以将prof文件转换为hprof文件格式。(hprof-conv命令其实是Android SDK 提供的一个工具,如果没有配置hprof-conv命令,可以去Android SDK 目录下的platform-tools目录下执行命令)转换完成之后的文件就可以被Mat打开了。

在这里插入图片描述

Mat分析

打开转换之后的文件
Android内存泄漏 -- Terminal + Mat_第1张图片
这个面板里面的各种介绍和分析这里就不展开了,网上有很多文章。点击下发的Action里面的 Domain Tree可以看到堆内存中的存留的对象和引用节点树。
Android内存泄漏 -- Terminal + Mat_第2张图片
我们可以在上面输入需要确认的是否有内存泄漏的类的类名,然后可以列出和改类名相关的所有的内存节信息。
Android内存泄漏 -- Terminal + Mat_第3张图片如图,这里就打印出了MainActivity相关的内存信息,里面有class 节点(图片上面有一个C 的),对象节点(图标上面没有C 的),内部类对象节点(对象节点中后面有$X的)。选中对象节点,右键->Path To GC Roots-> exclude weak references 可以列出来改对象是被那些对象引用从而导致无法回收的。
Android内存泄漏 -- Terminal + Mat_第4张图片

XXXXXActivity$onNewCreate$1 @ 0x167518f0 这里可以看出,该Activity对象是被其onNewCreate方法里面的一个匿名对象引用导致的。然后依次类推,结合这些信息和代码,很容易找到原因。只要断开引用链上的任何一环,都可以解决Activity对象被引用导致无法回收的问题。

总结

整理一下用到的命令

adb shell dumpsys meminfo <packageName> // 触发GC
adb shell am dumpheap <packageName>     // 导出heap信息
adb pull <srcFile> <destFile>           // 将手机里的prof文件拉到电脑上
hprof-conv <导出的prof文件> <格式转换之后的hprof文件>   // 将prof格式文件转换为mat接受的hprof文件

你可能感兴趣的:(Android开发,android,内存泄漏,内存优化)