LeakCanary源码解析

LeakCanary是square公司发布的一款检测Activity内存泄漏的工具。项目地址

1 设计

1.1 类图

类图

1.2 时序图

时序图

2.核心类功能介绍

2.1 LeakCanary.java

LeakCanary主要是一个工具类,构建RefWatcher对象。
(1)主要函数

1
2
3
public static RefWatcher install(Application application);
public static String leakInfo(Context context, HeapDump heapDump, AnalysisResult result,
boolean detailed);

第一个方法主要是初始化安装LeakCanary模块,还包括一些舒适化参数的构建。
第二个方法是通过最后分析的Dump文件和结果构建输入字符串。这个方法后面在Activity上显示会使用到。

2.2 RefWatcherBuilder.java

这个类主要是构建RefWatcher的包装类,它有个子类ActivityRefWatcher包含一些对Android的适配, 具体方法这里忽略,可以看下面RefWatcher包含成员变量。

2.3 ActivityRefWatcher.java

这个类主要是创建App ActivityLifecycleCallbacks监听Activity destory事件,然后调用RefWatcher分析回收问题。
(1)主要函数

1
2
3
4
5
6
7
8
9
10
11
12
public void watchActivities() {
// Make sure you don't get installed twice.
stopWatchingActivities();
application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
}

public void stopWatchingActivities() {
application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks);
}
void onActivityDestroyed(Activity activity) {
refWatcher.watch(activity);
}

可以看到主要是通过application.registerActivityLifecycleCallbacks的方式监听Activity生命周期,然后调用refWatcher.watch(activity)

2.4 RefWatcher.java

这个类是核心类,主要分析Activity是否被回收和生成Dump文件。
(1)主要成员

1
2
3
4
5
6
7
8
private final WatchExecutor watchExecutor;
private final DebuggerControl debuggerControl;
private final GcTrigger gcTrigger;
private final HeapDumper heapDumper;
private final Set<String> retainedKeys;
private final ReferenceQueue<Object> queue;
private final HeapDump.Listener heapdumpListener;
private final ExcludedRefs excludedRefs;

watchExecutor:执行回收和dump文件处理的线程提供者。子类有AndroidWatchExecutor其实就是在子线程中处理。
debuggerControl: 主要判断当前APP是否以Debug模式运行。
gcTrigger:调用GC方法
heapDumper:生成内存快照文件
retainedKeys: 存放key值,每个key对应一个Activity的虚引用。
queue: 负责存放虚引用对象释放完的队列。
heapdumpListener: 处理Dump信息
excludedRefs: 忽略一个内存泄漏的方法。
(2)主要方法

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
Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
...
removeWeaklyReachableReferences();

if (debuggerControl.isDebuggerAttached()) {
// The debugger can create false leaks.
return RETRY;
}
if (gone(reference)) {
return DONE;
}
gcTrigger.runGc();
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);
heapdumpListener.analyze(
new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
gcDurationMs, heapDumpDurationMs));
}
return DONE;
}

这个涉及到Java中虚引用的概念,可google详细了解。
首先直接判断Activity是否释放,如果释放直接结束,没有的话就调用系统GC,再判断Activity是否释放,如果释放直接结束,没有的话生成DumpFile,然后交给HeapdumpListener处理。

2.5 HeapAnalyzerService.java

解析DumpFile文件的后台服务

2.6 HeapAnalyzer.java

解析DumpFile文件工具类
(1)主要方法

1
public AnalysisResult checkForLeak(File heapDumpFile, String referenceKey)

解析过程较为复杂,有兴趣的可以了解下。主要是解析Hprof文件,GCRoots.

2.7 AnalysisResult.java

保存分析结果的存储对象。
(1)主要成员

1
2
3
4
5
6
7
public final boolean leakFound;
public final boolean excludedLeak;
public final String className;
public final LeakTrace leakTrace;
public final Throwable failure;
public final long retainedHeapSize;
public final long analysisDurationMs;

2.8 DisplayLeakService.java

主要负责通知栏提醒和保存分析结果。

2.9 其他

其他模块主要是对结果进行展示的逻辑,这里就不一一分析。

3 杂谈

1.这里是通过手动触发GC,然后判断回收,手动触发GC会打乱系统自动回收,影响APP性能,所以注意不要发布到正式包。
2.那需要思考的正式环境想统计内存泄漏问题怎么办?自己有个思路但是有缺陷
(1)开启一个线程阻塞ReferenceQueue.remove(), 然后定时(5分钟)去读取retainedKeys中的未被回收的Activity。
(2)但是Android系统回收无规律和没有事件通知,不知道系统到底是否已经触发回收。