这一篇博客主要记录一下LeakCanary的使用方式。
一、LeakCanary简介
目前,Java程序最常用的内存分析工具是MAT。
MAT的使用比较简单,但分析对应数据的步骤较为繁琐,结果也不够直观。
针对这个情况,Square公司为Android开发者,供了一个自动检测内存泄漏的工具,即LeakCanary。
LeakCanary被集成到Android工程后,一旦发现应用出现内存泄露问题,
就会主动弹出一个通知,然后在通知拉起的界面上,显示出问题的详细信息。
LeakCanary是一个开源工具,感兴趣的朋友可以研究一下它的源码,
由于我并没有深入分析过,因此此处仅结合网上的资料,简要记录一下它的原理。
1.1 发现潜在泄漏对象
LeakCanary被使用后,将会创建一个RefWatcher对象。
RefWatcher对象的watch方法被调用后,将会使用WeakReference引用Activity、Fragment等对象。
同时,RefWatcher会为这些对象生成唯一的Key值,然后将每个对象的Key、WeakReference与ReferenceQueue关联起来。
此外,LeakCanary会创建一个集合retainedKeys保存所有对象的Key值。
系统每次GC时,LeakCanary都可以扫描ReferenceQueue,
得到被释放对象的WeakReference(弱引用对象被释放后,对应的引用将被添加到ReferenceQueue中)。
然后,根据映射关系,LeakCanary得到WeakReference对应的Key值,
并将这些Key值从retainedKeys中移除。
于是,每次GC后,retainedKeys中剩余Key对应的对象,就是潜在的内存泄漏对象。
1.2 分析引用链
LeakCanary找到未被释放的对象后,必须分析该对象的引用关系,
才能确定该对象是否会导致内存泄漏。
为了完成这部分工作:LeakCanary会利用VMDebug与HAHA生成对象的最短引用链。
这一部分工作的依据是:VM 会有堆内各个对象的引用情况,并能以 hprof 文件导出。
HAHA作为square提供的Android 堆分析库,可以分析hprof文件生成Snapshot。
最终,LeakCanary利用Snapshot查询对象的最短引用链。
得到最短引用链后,LeakCanary就会弹出通知,将信息显示在界面上。
根据引用链,我们就可以比较快速的定位和分析问题。
1.3 整体流程图
上述过程整体的流程图基本上可以总结为下图,这里偷懒盗了个图:
二、使用LeakCanary
简单了解LeakCanary的原理后,我们就可以看看如何使用LeakCanary了。
2.1 导入LeakCanary对应的Jar包
在使用LeakCanary前,首先需要导入对应的Jar包。
以Android Studio为例,我们可以直接open module settings,
然后在Dependencies中搜索并增加leakCanary所需的库。
正常情况下,我们需要同时引入release和debug版本的库,
然后通过配置build.gradle,使得leakCanary仅在debug版本工作。
例如,在build.gradle中增加以下信息:
dependencies {
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.x'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.x'
}
2.2 监控Activity是否泄露
导入LeakCanary后,监控Activity是否泄露的操作极其简单。
我们只需要重写Application类的onCreate方法即可,如下所示:
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
//仅需这步操作即可
//返回一个预定义的 RefWatcher
//同时也会启用一个ActivityRefWatcher,自动监控调用Activity.onDestroy()之后泄露的activity
LeakCanary.install(this);
..................
}
}
如果检测到某个 activity 有内存泄露 ,LeakCanary 就是自动地显示一个通知。
2.3 监控其它对象是否泄露
2.2中提到的方法,主要用于检测应用中的Activity是否泄露。
如果需要监控应用中的其它对象是否泄露,则需要主动观察该对象。
当然,这部分工作也及其容易。
我们以监控Fragment为例,看看对应的操作:
public class App extends Application {
//Application为整个应用保存全局的RefWatcher
private RefWatcher refWatcher;
@Override
public void onCreate() {
super.onCreate();
refWatcher = LeakCanary.install(this);
}
public static RefWatcher getRefWatcher(Context context) {
App application = (App) context.getApplicationContext();
return application.refWatcher;
}
}
之后就可以使用 RefWatcher监控 Fragment了,例如:
public abstract class BaseFragment extends Fragment {
@Override
public void onDestroy() {
super.onDestroy();
RefWatcher refWatcher = App.getRefWatcher(getActivity());
refWatcher.watch(this);
}
}