一、什么是LeakCanary
leakCanary是Square开源框架,是一个Android和Java的内存泄露检测库。如果检测到某个activity有内存泄露,LeakCanary就是自动地显示一个通知,所以可以把它理解为傻瓜式的内存泄露检测工具。通过它可以大幅度减少开发中遇到的oom问题,大大提高APP的质量。
二、如何集成使用
Leakcanary官方链接
① LeakCanary2.0 之前
在 LeakCanary2.0 之前
我们接入的时候需要在 Application.onCreate 方法中显式调用 LeakCanary.install(this)
; 开启 LeakCanary 的内存监控。
1、在build.gradle中添加LeakCanary的依赖包
dependencies {
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.6.1'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.1'
}
2、在我们自定义Application的onCreate方法中注册LeakCanary
public class App extends Application {
private RefWatcher mRefWatcher;
@Override
public void onCreate() {
super.onCreate();
if (LeakCanary.isInAnalyzerProcess(this)) {
// This process is dedicated to LeakCanary for heap analysis.
// You should not init your app in this process.
return;
}
mRefWatcher = LeakCanary.install(this);
}
public static RefWatcher getRefWatcher(Context context) {
App application = (App) context.getApplicationContext();
return application.mRefWatcher;
}
}
正常情况下Application调用LeakCanary.install(this)后就可以正常监听该app程序的内存泄漏,如果想让LeakCanary监听指定对象的内存泄漏,我们就需要使用到RefWatcher的watch功能。
3、LeakCanary监听指定对象的内存泄漏
为了方便演示使用LeakCanary获取和解决内存泄漏的问题,我们先写一个内存泄漏的场景,我们知道最常见的内存泄漏是单列模式使用Activity的Context场景,所以我们也用单列模式来演示:
public class Singleton {
private static Singleton singleton;
private Context context;
private Singleton(Context context) {
this.context = context;
}
public static Singleton newInstance(Context context) {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null){//双重检查锁定
singleton = new Singleton(context);
}
}
}
return singleton;
}
}
在需要监听的对象中调用RefWatcher的watch方法进行监听,比如我想监听一个Activity,我们可以在该Acitivity中onCreate方法中添加getRefWatcher().watch(this);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
RefWatcher refWatcher = App.getRefWatcher(getActivity());
refWatcher.watch(this);
setContentView(R.layout.activity_second);
Singleton singleton = Singleton.newInstance(this);
}
② LeakCanary2.0 之后
从LeakCanary2.0
开始通过库里注册的 ContentProvier 自己开启 LeakCanary 的内存监控,无需用户手动再添加初始化代码
。
dependencies {
// debugImplementation because LeakCanary should only run in debug builds.
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'
}
通过过滤 Logcat 中的 LeakCanary 标签来确认 LeakCanary 在启动时正在运行:
D/LeakCanary: LeakCanary is running and ready to detect memory leaks.
三、LeakCanary 使用
以下面的非静态内部类的泄漏场景为例,演示下 LeakCanary 的使用步骤。
public class MainActivity extends AppCompatActivity {
//非静态内部类的静态实例引用
public static InnerClass innerClass = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//保证非静态内部类的实例只有1个
if (innerClass == null) {
innerClass = new InnerClass();
}
}
// 非静态内部类
private class InnerClass {
//...
}
}
安装测试应用后,手机上会出现如下两个图标:
左侧的是测试应用的图标,右侧是自动安装的 LeakCanary 的图标。
打开测试应用,然后返回退出,人为制造内存泄漏。等待大概10秒,LeakCanary 就会检测到,并进行分析。在通知栏可以看到进度。
然后在分析完成后,然后在通知栏会收到通知。
点开通知,会打开 LeakCanary 的界面。
每行对应一组具有相同签名的泄漏。LeakCanary 在应用程序第一次使用该签名触发泄漏时将一行标记为“New”。点击可以查看泄漏详情。
这里显示 com.example.leaksample.MainActivity 这个实例发生了泄漏,红色下划波浪线标记的是可能的原因。我们分析下原因:innerClass 是非静态内部类的静态实例,它的生命周期等于应用的生命周期,持有外部类 MainActivity 的引用,故 MainActivity 无法被 GC 回收,从而导致内存泄漏。解决方案就是将 InnerClass 改为静态内部类。
另外,在 Logcat 中也可以看到 LeakCanary 的相关信息。LeakCanary 将应用中发现的泄漏分为两类:应用程序泄漏和库泄漏。
====================================
HEAP ANALYSIS RESULT
====================================
1 APPLICATION LEAKS
......
====================================
0 LIBRARY LEAK
如果有库泄漏,LeakCanary 会在其泄漏列表中将其标记为 Library Leak:
以上就是 LeakCanary 的使用说明, LeakCanary 只能定位到内存泄漏的大概范围,具体原因还是需要我们自己分析。内存泄露一般是代码设计存在缺陷导致的,只有多了解内存泄露的场景,才可以避免不必要的内存溢出和提高自己的编码水平。
四、LeakCanary 原理
LeakCanary 是通过在 Application 的 registerActivityLifecycleCallbacks 方法实现对 Activity 销毁监听的,该方法主要用来统一管理所有 Activity 的生命周期。所有 Activity 在销毁时在其 OnDestory 方法中都会回调 ActivityLifecycleCallbacks 的 onActivityDestroyed 方法,而 LeakCanary 要做的就是在该方法中调用 RefWatcher.watch 方法实现对 Activity 进行内存泄漏监控。
那么,LeakCanary 是如何判断某个 Activity 可能会发生内存泄漏呢?答案是:WeakReference 和 ReferenceQueue,即 LeakCanary 利用了 Java 的 WeakReference 和 ReferenceQueue,通过将 Activity 包装到 WeakReference 中,被 WeakReference 包装过的 Activity 对象如果能够被回收,则说明引用可达,垃圾回收器就会将该 WeakReference 引用存放到 ReferenceQueue 中。假如我们要监视某个 Activity 对象,LeakCanary 就会去 ReferenceQueue 找这个对象的引用,如果找到了,说明该对象是引用可达的,能被 GC 回收,如果没有找到,说明该对象有可能发生了内存泄漏。最后,LeakCanary 会将 Java 堆转储到一个 .hprof 文件中,再使用 Shark(堆分析工具)析 .hprof 文件并定位堆转储中“滞留”的对象,并对每个"滞留"的对象找出 GC roots 的最短强引用路径,并确定是否是泄露,如果泄漏,建立导致泄露的引用链。最后,再将分析完毕的结果以通知的形式展现出来。
五、参考
-
LeakCanary 中文使用说明
-
LeakCanary: 让内存泄露无所遁形
-
Android LeakCanary使用详细教程
-
Android LeakCanary的使用和原理
-
Android 内存泄漏检测工具 LeakCanary 的使用
-
LeakCanary 使用指南 (1)
-
Android 性能优化之利用 LeakCanary 检测内存泄漏及解决办法