2018-01-17

LeakCanary 源码学习

https://github.com/square/leakcanary

跟踪过程遇到的一些的类

  • AndroidRefWatcherBuilder: LeakCanary.install 内部具体执行者,构建初始化
  • RefWatcher/ActivityRefWatcher:判断是否产生内存泄漏 / 专供 Android 使用
  • AndroidWatchExecutor: 触发 RefWatcher 检查
  • AndroidHeapDumper:当 RefWatcher 发现内存泄漏,调用 dumper.dumpHeap() 导出文件
  • ServiceHeapDumpListener: 然后调用 listener.analyze(heapDump:HeapDump) 通知分析数据
  • DisplayLeakService:显示结果
  • HeapAnalyzerService:分析
  • HeapAnalyzer:分析负责人

RefWatcher

Builder 里边初始化了一堆东西,大部分都传给 RefWatcher 构造函数了。RefWatcher 提供了一个 watch 方法;如果一个对象生命周期结束了(比如Activity.onDestroy()之后),watch 这个对象,RefWatcher 内部会使用 KeyedWeakReference 将其包装一下,这样我们就可以得到一个 key ,后面根据这个 key 就能知道对象有没有释放。

先插一句 WeakReference 和 ReferenceQueue 的用法。
WeakReference 构造函数可以传入一个 ReferenceQueue, WeakReference 引用的对象被 GC 回收后,这个 WeakReference 就会被加入队列,所以检查 ReferenceQueue 队列就能知道 WeakReference 引用的对象有没有被释放。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/**
* retainedKeys:Set<String> 保存 key 用的
* 将对象封装为 KeyedWeakReference
* key 保存到 retainedKeys
* 触发检查对象是否被回收
*/
public void watch(Object watchedReference, String referenceName) {
String key = UUID.randomUUID().toString();
retainedKeys.add(key);
final KeyedWeakReference reference =
new KeyedWeakReference(watchedReference, key, referenceName, queue);

ensureGoneAsync(watchStartNanoTime, reference);
}
/**
* 先检查一遍有没有回收,如果被回收了那咱们到这就 ok
* 如果没被回收就触发 GC,再检查一遍
* 还有的话那你就是内存泄漏了,dump heap 触发通知逻辑
*/
Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
removeWeaklyReachableReferences()
if (gone(reference)) {
return DONE;
}
gcTrigger.runGc();
removeWeaklyReachableReferences()
if (!gone(reference)) {
// dump heap & notify
}
}
/**
* 遍历 ReferenceQueue,将已被回收的对象移除
*/
private void removeWeaklyReachableReferences() {
KeyedWeakReference ref;
while ((ref = (KeyedWeakReference) queue.poll()) != null) {
retainedKeys.remove(ref.key);
}
}
/**
* 判断是否被回收
*/
private boolean gone(KeyedWeakReference reference) {
return !retainedKeys.contains(reference.key);
}

AndroidHeapDumper

1
2
// 一句话,拿到 dumpFile
android.os.Debug.dumpHprofData(heapDumpFile.getAbsolutePath());

HeapAnalyzer

调用 square haha 这个库负责分析
大概步骤:

  • 解析 dump 文件
  • 先拿到所有 KeyedWeakReference 相关内容,再根据 key 进行搜索,拿到 reference
  • 搞定泄露的 reference 就可以拿到具体路径了
  • 封装成 AnalysisResult 返回
  • 通知栏提示

整个 LeakCanary 的设计清晰明了,跟踪代码时逻辑抽象封装非常到位,厉害。