用 LeakCannary 也不短时间了,源码还没仔细研究过,很好奇他是如何监听内存泄漏,并且能显示到界面上的过程,所以我就带着问题去查看源码来一探究竟,来满足我的好奇心。
- 如何监听内存泄漏,具体流程是什么?
首先我们建立一个简单的项目,按照官方的文档,我们开始在 Application 中初始化,然后调用LeakCanary 的install的方法。
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
LeakCanary.install(this);
}
}
下边我们追踪一下 LeakCanary里边的方法install() 方法,我们看到了我们所熟悉的 builder 模式,开始构造方法,最终调用buildAndInstall()返回RefWatcher。
public static RefWatcher install(Application application) {
return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
.buildAndInstall();
}
为了我们开始追踪源码buildAndInstall() 方法,最终会追踪到ActivityrefWathch,然后我们仔细分析一下这个,如何和生命周期互相绑定的呢,我们分析一下
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new Application.ActivityLifecycleCallbacks() {
...
@Override public void onActivityDestroyed(Activity activity) {
ActivityRefWatcher.this.onActivityDestroyed(activity);
}
};
public void watchActivities() {
// Make sure you don't get installed twice.
stopWatchingActivities();
application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
}
通过上边方法我们可以看到通过,流程大概如下,首先在Application 中初始化,它会生成 refWatch 并通过application 来监听 Activity 的生命周期中的 onDestroy 方法,然后我们在追踪一下 onActivityDestroyed 的方法,就是在页面销毁的时候我们进行如何的操作才能监听到对象的回收。
void onActivityDestroyed(Activity activity) {
refWatcher.watch(activity);
}
public void watch(Object watchedReference) {
watch(watchedReference, "");
}
public void watch(Object watchedReference, String referenceName) {
if (this == DISABLED) {
return;
}
checkNotNull(watchedReference, "watchedReference");
checkNotNull(referenceName, "referenceName");
final long watchStartNanoTime = System.nanoTime();
// 生成一个随机 UUID
String key = UUID.randomUUID().toString();
// retainedKeys (Set<String> 的集合)
retainedKeys.add(key);
// 这里是将对象,随机key,名字,还有弱引用队列,然后生成一个自定义弱引用
final KeyedWeakReference reference =
new KeyedWeakReference(watchedReference, key, referenceName, queue);
// 异步进行检查是否内存泄漏
ensureGoneAsync(watchStartNanoTime, reference);
}
然后我们继续追踪 ensureGoneAsync 方法,我们发现,他启动了线程池来调用工作线程来工作。
private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
watchExecutor.execute(new Retryable() {
@Override public Retryable.Result run() {
return ensureGone(reference, watchStartNanoTime);
}
});
}
我们继续看看线程的run() 方法中的 ensureGone() 进行了什么操作
Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
// gc 的启动开始时间
long gcStartNanoTime = System.nanoTime();
// 计算监听的时间差值
long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
// 遍历 queue 队列,然后一个个移除
removeWeaklyReachableReferences();
// 检测是否是正在调试的状态
if (debuggerControl.isDebuggerAttached()) {
// The debugger can create false leaks.
return RETRY;
}
// 判断 retainedKeys 中是否reference.key 如果没有的话表名没有泄露,否则继续进行
if (gone(reference)) {
return DONE;
}
// 再次进行一次 GC
gcTrigger.runGc();
removeWeaklyReachableReferences();
// 判断是否内存泄漏,如果为 true 表明内存泄漏了,这样就会下边继续分析具体哪一部分代码内存泄漏
if (!gone(reference)) {
long startDumpHeap = System.nanoTime();
long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
File heapDumpFile = heapDumper.dumpHeap();
if (heapDumpFile == RETRY_LATER) {
// Could not dump the heap.
return RETRY;
}
long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
heapdumpListener.analyze(
new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
gcDurationMs, heapDumpDurationMs));
}
return DONE;
}
小结
我们可以发现就是 LeakCanary 所做的就是监听 Activity 中的 onDestroy方法,然后将引用的 Activity 进行封装为一个弱引用对象,然后进行一次 Gc 回收,引用队列里边还有这个对象,就表明内存泄漏,否则就是被正常gc 回收了。