19年年末总结一篇《LeakCanary原理从0到1》,当时还比较满意,以为自己就比较了解这个框架了,Too young, Too Simple。
周五群里一个小伙伴问:“线上做内存泄漏检测大家有什么思路吗?”。
内存泄漏检测首先想到的是 LeakCanary,可以看看能从LeakCanary上找到一些思路吗?
本文并不是从0开始解释 LeakCanary 的工作原理,所以为了阅读体验更佳,还不太了解 LeakCanary 是怎样判定对象内存泄漏的读者,可以先从《LeakCanary原理从0到1》开始阅读。
本文将从内存泄漏后 LeakCanary 的后续工作开始讲起,分析 LeakCanary 是怎么找到泄漏对象的强引用链的,分析 LeakCanary 不能直接用于线上内存检测的原因,并尝试找出线上检测内存泄漏的一些思路。
在判定有内存泄漏后,「LeakCanary」调用将系统提供的Debug.dumpHprofData(File file)
函数,改函数将生成一个虚拟机的内存快照,文件格式为 .hprof
,这个dump文件大小通常有10+M。(本文中所说的dump文件都是指内存快照的.hprof
文件)
//:RefWatcher.java
Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime)
...
//内部调用Debug.dumpHprofData(File file)函数
File heapDumpFile = heapDumper.dumpHeap(file);
HeapDump heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile)
.referenceKey(reference.key)
.build();
heapdumpListener.analyze(heapDump);
....
return DONE;
}
生成dump文件后,LeakCanary 将被泄露对象的 referenceKey 与 dump 文件 对象封装在 HeapDump 对象中,然后交给ServiceHeapDumpListener
处理,在ServiceHeapDumpListener
中创建 leakcanary 进程并启动服务 HeapAnalyzerService
。
dump 文件的解析工作是在HeapAnalyzerService
中完成的,主要逻辑入下:
//HeapAnalyzerService.java
//创建一个分析器
HeapAnalyzer heapAnalyzer =
new HeapAnalyzer(heapDump.excludedRefs, this, heapDump.reachabilityInspectorClasses);
//使用分析器分析dump文件,得到分析结果
AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey,
heapDump.computeRetainedHeapSize);
//将分析结果交由listener组件处理
AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
下面继续跟踪分析器逻辑,主要查看 HeapAnalyzer
的 checkForLeak
方法:
//: HeapAnalyer.java
public AnalysisResult checkForLeak(File heapDumpFile, String referenceKey,
boolean computeRetainedSize) {
//读取dump文件,解析文件内容并生成一个Snapshot对象
HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile);
HprofParser parser = new HprofParser(buffer);
Snapshot snapshot = parser.parse();
//消除重复的GcRoots对象
deduplicateGcRoots(snapshot);
//通过referenceKey 在Snapshot对象中找到泄漏对象
Instance leakingRef = findLeakingReference(referenceKey, snapshot);
//找到泄漏路径
return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef, computeRetainedSize);
}
在checkForLeak
方法中:
Snapshot
对象当中,这种就可以从Snapshot
中获得JVM的内存信息。(关于dump文件格式,有兴趣的可以点击这里,同时也可去看 square 的 com.squareup.haha:haha+
,LeakCanary 使用的就是这个 dump 解析库)。Snapshot
中类名为 KeyedWeakReference
且 referenceKey
所对应的泄漏对象 Instence
。Snapshot
中寻找泄漏对象 Instence
的泄漏强引用链泄漏对象的引用链式如何被找到的呢?下面继续分析 findLeakTrace
方法:
//: HeapAnalyer.java
private AnalysisResult findLeakTrace(long analysisStartNanoTime, Snapshot snapshot,
Instance leakingRef, boolean computeRetainedSize) {