LeakCanary是一个检测内存泄漏的工具,使用非常简单。主要用来检测Activity和Fragment内存泄漏,如果发生内存泄漏,直接在用UI显示哪里发生了泄漏并展示对象引用链。
LeakCanary地址:https://github.com/square/leakcanary
LeakCanary的使用
在gradle文件中加入依赖
debugImplementation 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.3'
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.3'
// 如果使用了 support fragment,需要依赖
debugImplementation 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.3'
在Application中加入
public class MyApplication extends Application {
private RefWatcher refWatcher = null;
@Override
public void onCreate() {
super.onCreate();
if (!LeakCanary.isInAnalyzerProcess(this)) {
// 是否在主进程
refWatcher = LeakCanary.install(this);
}
}
}
如果想监视其他的Object是否内存泄漏了
refWatcher.watch(Object object)
原理
在LeakCanary 的 wiki 就简述了基本的原理:
- RefWatcher.watch() 为监视的对象创建一个 KeyedWeakReference;
- 然后,后台线程会检查引用是否被清除回收了,如果没有就触发GC;
- 如果引用还没被清除,则堆内存dump到文件系统,成一个.hprof文件
- HeapAnalyzerService运行在另外一个进程,其中内部的HeapAnalyzer 使用HAHA库解析heap dump
- 根据reference key,HeapAnalyzer 找到对应的 KeyedWeakReference,定位内存泄露
- HeapAnalyzer 可以找出 GC roots 的最短强引用路径,并确定是否是泄露。如果泄漏,建立导致泄露的引用链。
- 将泄漏结果(引用链)传给App进程DisplayLeakService,通知展示
我们主要看的就是LeakCanary.install(this)
到底发生了什么?
在解析流程之前先看下涉及到的几个重要的类:
-
RefWatcher: 核心类,负责管理和提供入口watch(),由AndroidRefWatcherBuilder创建RefWatcher,建造者模式
-
WatchExecutor: 负责控制执行检测内存泄漏任务
-
DebuggerControl:判断是否处于debug
-
GcTrigger:负责触发一次GC
-
HeapDump:表示指定时刻的堆栈的快照,AndroidHeapDump为子类
-
HeapDump.Builder: 负责创建HeapDump
-
HeapDump.Listener:监听器,当发生内存泄漏的时候,会收到消息,需要触发分析AndroidHeapDump任务
-
ServiceHeapDumpListener:HeapDump.Listener的实现类,当触发分析任务,调用HeapAnalyzerService执行分析任务
-
HeapAnalyzerService:是一个Android中四大组件之一的Service,运行在独立的进程,负责执行分析任务和UI通知
-
HeapAnalyzer:在HeapAnalyzerService内部中,是对DumpHeap分析内存泄漏和找出引用链的工具
-
retainKeys: 是一个Set,保存着当前还没被回收的Reference的key
-
ReferenceQueue:引用队列,WeakReference可以关联引用队列,当reference被回收时,会被加入到ReferenceQueue,这样我们就可以判断哪些对象没有被回收了
-
DisplayLeakService:记录泄漏日志和展示通知的Service
其实,leakCanary的基本原理就是利用ReferenceQueue,在Activity销毁的时候判断对象有没有被加入ReferenceQueue,若没有则说明Activity还在存活,可能存在泄漏。
GC Root的种类
java 使用引用链法来判断一个引用是否该被回收,而出发点就是GC Root
- 虚拟机栈的对象引用
- 本地方法栈的对象引用
- 方法区的常量引用
- 方法区的静态对象引用
源码分析
isInAnalyzerProcess()
方法是用来判断当前的进程是否是在分析进程,因为多进程中,每个进程都会创建Application,而我们需要在非LeakCanary的进程中检测。
// LeakCanary
public static boolean isInAnalyzerProcess(@NonNull Context context) {
Boolean isInAnalyzerProcess = LeakCanaryInternals.isInAnalyzerProcess;
// This only needs to be computed once per process.
if (isInAnalyzerProcess == null) {
isInAnalyzerProcess = isInServiceProcess(context, HeapAnalyzerService.class);
LeakCanaryInternals.isInAnalyzerProcess = isInAnalyzerProcess;
}
return isInAnalyzerProcess;
}
install方法是LeakCanary的总入口
public static @NonNull RefWatcher install(@NonNull Application application) {
return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
.buildAndInstall();
}
这里使用了Build建造者模式,利用AndroidRefWatcherBuilder创建RefWatcher
我们分成几个部分:
- refWatch(application) 创建AndroidRefWatcherBuilder
- listenerServiceClass(DisplayLeakService.class) 设置监听内存泄漏和分析结果的 Service
- excludedRefs(AndroidExcludedRefs.createAppDefaults().build()) 设置忽略的内存泄漏的错误
- buildAndInstall() 初始化AndroidRefWatcher
1. refWatch(application)
// LeakCanary
public static @NonNull AndroidRefWatcherBuilder refWatcher(@NonNull Context context) {
return new AndroidRefWatcherBuilder(context);
}
这里只是创建了AndroidRefWatcherBuilder,最后使用Build模式创建AndroidRefWatcher
2.listenerServiceClass(DisplayLeakService.class)
// AndroidRefWatcherBuilder
public @NonNull AndroidRefWatcherBuilder listenerServiceClass(
@NonNull Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
enableDisplayLeakActivity = DisplayLeakService.class.isAssignableFrom(listenerServiceClass);
return heapDumpListener(new ServiceHeapDumpListener(context, listenerServiceClass));
}
创建了heapDumpListener负责heapDump的分析和处理
isAssignableFrom() 是object方法,
例如 a.isAssignableFrom(b) 判断a是不是b的父类或接口
如果listenerServiceClass继承或者是DisplayLeakService,则enableDisplayLeakActiviy为true,表示显示LeakCanary界面
// ServiceHeapDumpListener
public final class ServiceHeapDumpListener implements HeapDump.Listener {
private final Context context;
private final Class<? extends AbstractAnalysisResultService> listenerServiceClass;
public ServiceHeapDumpListener(@NonNull final Context context,
@NonNull final Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
this.listenerServiceClass = checkNotNull(listenerServiceClass, "listenerServiceClass");
this.context = checkNotNull(context, "context").getApplicationContext();
}
@Override public void analyze(@NonNull HeapDump heapDump) {
checkNotNull(heapDump, "heapDump");
HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
}
}
这里简单看一下ServiceHeapDumpListener ,主要方法在analyze()
,实际上也只是通知了HeapAnalyzerService.runAnalysis()去分析heapDump;
HeapAnalyzerService是运行在独立进程的Service。它是一个IntentService,执行完一次任务就会终止。
3. excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
AndroidExcludedRefs.java类中维护着一系列的特定的内存泄漏类型,在执行内存泄漏和显示的时候也会忽略这些类