LeakCanary源码分析
LeakCanary是一个Android内存泄露自动分析工具,具有简单易用,结果可读性强,不仅适用于Android开发人员,也适用于测试人员使用。能快速提高软件质量。github地址:https://github.com/square/leakcanary
leakCanary包含3个模块leakcanary-android(leakcanary-android-no-op),leakcanary-watcher和leakcanary-analyzer。
leakcanary-android debug使用。负责启动内存泄露检测,给别的应用提供接口,包括启动和分析结果展示。
leakcanary-android-no-op 和 leakcanary-android一样,一般用在release版本中。
leakcanary-watcher 检测对象是否发生内存泄露,如果发生内存泄露则导出堆信息到本地文件中(*.hprof)
leakcanary-analyzer 从*.hprof文件中分析对象发生内存泄露原因,找出哪些对象强引用者监听对象。并将结果告诉leakcanary-android。
一. 流程图
二. 时序图
三. 重要类分析
1 LeakCanary: 入口类
/**启动对Activity内存泄露监听**/
public static RefWatcher install(Application application);
/**根据内存泄露分析结果,生成面向人的分析报告**/
public static String leakInfo(Context context, HeapDump heapDump, AnalysisResult result,boolean detailed);
2 ActivityRefWatcher:这是一个代理类,主要功能是:注册ActivityLifecycleCallbacks回调,当Activity执行ondestroy()前,启动RefWatcher检测该Activity是否发生内存泄露。
3 RefWatcher:内存泄露检测核心类
/**
* Watches the provided references and checks if it can be GCed. This method is non blocking,
* the check is done on the {@link Executor} this {@link RefWatcher} has been constructed with.
*
* @param referenceName An logical identifier for the watched object.
*/
public void watch(Object watchedReference, String referenceName);
/**
*查看reference指向的对象是否发生内存泄露,如果发生泄露则启动HeapDump.dumpHeap()导出heap快照
*/
void ensureGone(KeyedWeakReference reference, long watchStartNanoTime);
4 AndroidHeapDumper 实现了接口HeapDumper,导出当前内存快照到文件中。使用了Android自带的Debug类导出内存快照。
/**
* 导出内存快照,保存到heapDumpFile文件中
*/
@Override public File dumpHeap() {
File heapDumpFile = getHeapDumpFile();
boolean fileCreated;
try {
fileCreated = heapDumpFile.createNewFile();
} catch (IOException e) {
cleanup();
CanaryLog.d(e, "Could not check if heap dump file exists");
return NO_DUMP;
}
Debug.dumpHprofData(heapDumpFile.getAbsolutePath());
return heapDumpFile;
}
Debug.dumpHprofData(heapDumpFile.getAbsolutePath());
5 KeyedWeakReference: WeakReference的子类
String key;//用时间戳来唯一标识一个弱引用
KeyedWeakReference(Object referent, String key, String name,
ReferenceQueue<Object> referenceQueue) {
super(checkNotNull(referent, "referent"), checkNotNull(referenceQueue, "referenceQueue"));
this.key = checkNotNull(key, "key");
this.name = checkNotNull(name, "name");
}
构造函数传了一个ReferenceQueue给父类WeakReference。ReferenceQueue的作用是,当被引用的对象被JVM释放的时候会在该队列中生成一条记录(把该对象的弱引用放到该队列中),即:我们通过检查ReferenceQueue中是否存在我们关心的KeyedWeakReference来判断引用的对象是否被成功释放。代码如下:
KeyedWeakReference ref;
boolean isLeak;
while ((ref = (KeyedWeakReference) queue.poll()) != null) {
retainedKeys.remove(ref.key);
isLeak = !(retainedKeys.contains(ref.key));
}
if(retainedKeys.contains(ref.key)){
isLeak = true;
}
6 ServiceHeapDumpListener:类存泄露监听器,当检测到发生内存,并且内存快照被导出到文件后,在该监听器中调用内存泄露分析工具。
7 HeapAnalyzerService:内存泄露分析Service,该类继承与IntentService,每个Intent在子线程中处理
8 HeapAnalyzer:类存泄露分析工具类
9 AbstractAnalysisResultService:负责接收内存泄露分析结果的Service
10 AnalysisResult:分析结果Model,结构如下:
/** True if a leak was found in the heap dump. */
public final boolean leakFound;
/**
* True if {@link #leakFound} is true and the only path to the leaking reference is
* through excluded references. Usually, that means you can safely ignore this report.
* 如果为true,表示该泄露不是应用发生的,可以忽略
*/
public final boolean excludedLeak;
/**
* Class name of the object that leaked if {@link #leakFound} is true, null otherwise.
* The class name format is the same as what would be returned by {@link Class#getName()}.
*/
public final String className;
/**
* Shortest path to GC roots for the leaking object if {@link #leakFound} is true, null
* otherwise. This can be used as a unique signature for the leak.
*/
public final LeakTrace leakTrace;
/** Null unless the analysis failed. */
public final Throwable failure;
/**
* The number of bytes which would be freed if all references to the leaking object were
* released. 0 if {@link #leakFound} is false.
*/
public final long retainedHeapSize;
/** Total time spent analyzing the heap. */
public final long analysisDurationMs;