LeakCanary 的使用
Posted ByteSaid
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeakCanary 的使用相关的知识,希望对你有一定的参考价值。
1 LeakCanary 简介
LeakCanary 是 Square 公司为 android 开发者提供的一个自动检测内存泄漏的工具,LeakCanary 本质上是一个基于 MAT 进行 Android 应用程序内存泄漏自动化检测的的开源工具,我们可以通过集成 LeakCanary 提供的 jar 包到自己的工程中,一旦检测到内存泄漏,LeakCanary 就会 dump Memory 信息,并通过另一个进程分析内存泄漏的信息并展示出来,随时发现和定位内存泄漏问题,极大地方便了Android应用程序的开发。
2 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 的最短强引用路径,并确定是否是泄露,如果泄漏,建立导致泄露的引用链。最后,再将分析完毕的结果以通知的形式展现出来。
3 LeakCanary 接入
要使用 LeakCanary,请将 leakcanary-android 依赖项添加到应用程序 build.gradle 文件中:
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.
在 LeakCanary2.0 之前我们接入的时候需要在 Application.onCreate 方法中显式调用 LeakCanary.install(this); 开启 LeakCanary 的内存监控。
从 LeakCanary2.0 开始通过库里注册的 ContentProvier 自己开启 LeakCanary 的内存监控,无需用户手动再添加初始化代码。
LeakCanary 自动检测以下对象的泄漏:
- 销毁的Activity实例
- 销毁的Fragment实例
- 销毁的片段View实例
- 清除ViewModel实例
4 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 的使用的主要内容,如果未能解决你的问题,请参考以下文章
工作中遇到的Android内存优化问题-leakcanary源码解析