2019-10-09:谈一谈LeakCanray的工作原理?
MoJieBlog opened this issue · 5 comments
LeakCanary 主要利用了弱引用的对象, 当 GC 回收了这个对象后, 会被放进 ReferenceQueue 中;
在页面消失, 也就是 activity.onDestroy 的时候, 判断利用 idleHandler 发送一条延时消息, 5秒之后,
分析 ReferenceQueue 中存在的引用, 如果当前 activity 仍在引用队列中, 则认为可能存在泄漏, 再利用系统类 VMDebug 提供的方法, 获取内存快照,
找出 GC roots 的最短强引用路径, 并确定是否是泄露, 如果泄漏, 建立导致泄露的引用链;
System.runFinalization(); // 强制调用已经失去引用的对象的 finalize 方法
Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime){
// 1.. 从 retainedKeys 移除掉已经被会回收的弱引用
removeWeaklyReachableReferences();
// 3.. 若当前引用不在 retainedKeys, 说明不存在内存泄漏
if (gone(reference)) {
return DONE;
}
// 4.. 触发一次gc
gcTrigger.runGc();
// 5.. 再次从 retainedKeys 移除掉已经被会回收的弱引用
removeWeaklyReachableReferences();
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);
HeapDump heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile).referenceKey(reference.key)
.referenceName(reference.name)
.watchDurationMs(watchDurationMs)
.gcDurationMs(gcDurationMs)
.heapDumpDurationMs(heapDumpDurationMs)
.build();
heapdumpListener.analyze(heapDump);
}
return DONE;
}
分析 ReferenceQueue 中存在的引用, 如果当前 activity 仍在引用队列中, 则认为可能存在泄漏, 再利用系统类 VMDebug 提供的方法, 获取内存快照,
如果该弱应用对象在引用队列中,则说明是被回收了.不在弱引用队列中的才是泄露的.
优秀文章
LeakCanary原理解析
当一个Activity的onDestory方法被执行后,说明该Activity的生命周期已经走完,在下次GC发生时,该Activity对象应将被回收。
通过上面对引用的学习,可以考虑在onDestory发生时创建一个弱引用指R向Activity,并关联一个RefrenceQuence,当Activity被正常回收,弱引用实例本身应该出现在该RefrenceQuence中,否则便可以判断该Activity存在内存泄漏。
通过Application.registerActivityLifecycleCallbacks()方法可以注册Activity生命周期的监听,每当一个Activity调用onDestroy进行页面销毁时,去获取到这个Activity的弱引用并关联一个ReferenceQuence,通过检测ReferenceQuence中是否存在该弱引用判断这个Activity对象是否正常回收。
当onDestory被调用后,初步观察到Activity未被GC正常回收时,手动触发一次GC,由于手动发起GC请求后并不会立即执行垃圾回收,所以需要在一定时延后再二次确认Activity是否已经回收,如果再次判断Activity对象未被回收,则表示Activity存在内存泄漏。