利用 StrictMode 和 MAT 解决内存泄漏
文章目录
前一阵子正好碰到了 App 里出现内存泄漏,记录一下完整的流程。
起因
在运行时检测 App 的内存泄漏有多种方式:比如手动的在 Android Studio 中调用 GC,然后观察内存抖动的情况;比如在 App 中加入 leakcanary 这样的 lib,当发生内存泄漏时直接输出 Heap Dump。不过我一直不希望在 App 中引入过多的 lib,所以我是通过 StrictMode 来监测 App 有没有发生内存泄漏。
StrictMode 是 Android 2.3 之后加入的一个类,用以检测程序中是否有违例情况发生的开发者工具。
在自定义的 Application 中的 onCreate()
方法中初始化 StrictMode 就可以了
public void onCreate() {
super.onCreate();
if (BuildConfig.DEBUG) {
// 设置线程策略和 VM策略
StrictMode.setThreadPolicy(new StrictMode
.ThreadPolicy
.Builder()
.detectAll()
.penaltyLog()
.build());
StrictMode.setVmPolicy(new StrictMode
.VmPolicy.
Builder().
detectAll().
penaltyLog().
build());
}
}
之后如果有内存泄漏、未关闭 closable 对象或在主线程中监测到耗时操作时,StrictMode 就会通过 log 输出来提醒我们。
利用 Android Studio 获取 Java Heap
之后,当某次发生内存泄漏时,StrictMode 就通过 log 提示我们。此处是我的 App 所遇到的情况,也就是存在多个 Activity 实例,并且无法释放。
此时可以通过 Android Studio 自带的 Monitor 来获取此时应用的堆信息。
- 点击
Initate GC
按钮,强制 GC - 点击
Dump Java Heap
按钮,生成 Heap 文件(*.hprof)
通常获取 Heap 文件之后会直接打开 *.hprof 文件,不过我们也可以在 Android Studio 左侧的Captures
标签找到已经生成的 Heap 文件。
- 打开 Heap 文件之后,首先打开右侧的
Analyzer Tasks
标签 - 点击
perform action
按钮 - 在
Analysis Results
里就可以看到具体有哪些 Acitvity 导致了内存泄漏 - 在
Reference Tree
里可以看到持有这个 Activity 引用的对象
如果是简单的情况的话,这里就可以排查出到底是那个引用导致的 Activity 无法释放,从而导致了内存泄漏。但是如果情况复杂一些的话,就需要用到 MAT 来分析了
利用 MAT 分析 Heap 文件
MAT 全称"Eclipse Memory Analyzer”,是一个强大 JAVA 堆转储文件分析工具,也就是分析 *.hprof 文件的工具。
在使用 MAT 之前,我们需要先将 Android Studio 生成的 Heap 文件转换为标准格式。
用 MAT 打开 Heap 文件之后点击生成 Histogram,可以列出内存中的对象及其个数、大小
由于我们已经通过 Android Studio我们已经知道泄漏的对象的类名。所以可以直接筛选出泄漏的对象。右键Merge Shortest Paths to GC Roots
-> exclude all phantom/weak/soft etc. references
。排除掉所有的虚/软/弱引用之后,就可以知道到底是那些对象持有泄漏的 Activity 的强引用从而导致内存泄漏了。
这里可以看到是 Glide 引起的,此时就可以通过代码中 Glide 相关的代码来排查。
此处的例子,是一个匿名内部类
BitmapTransformation
持有了 Activity 的引用,而 Glide 是全局配置的。所以 Activity 对象无法释放,从而导致了内存泄漏
排查以外
内存泄漏通常是由于我们代码的不规范所导致的。比如长引用持有对象而不释放啊,closable 对象未及时关闭啊,或者一些非静态类持有对象啊。
除了在内存泄漏发生之后,及时的通过工具来排查原因,修改代码之外,最重要的还是要规范自己的代码,提高代码质量。当然,不忽略任何一个 lint
提示的警告大概是最方便快捷的方式了吧!
文章作者 Dio.Ye
上次更新 2017-03-10