LeakCanary源码分析第二讲-RefWatcher详解
如果你已经阅读了LeakCanary源码分析第一讲,那么LeakCanary的基本架构应该已经掌握了。本文将详细分析RefWatcher的工作原理,当RefWatcher检查到引用路径不是弱通路的时候就会触发HeapDumper。
WeakReference和ReferenceQueue
要理解RefWatcher的工作原理,首先需要知道WeakReference。当GC线程扫描它所管辖的内存区域时,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否(这一点与SoftReference不同),都会回收它的内存。由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。WeakReference和ReferenceQueue联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。上图中的实线a表示强引用,虚线aa表示弱引用。如果切断a,那么Object对象将会被回收。正如下面这段测试代码所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | A a = new A ( ) ; ReferenceQueue queue = new ReferenceQueue ( ) ; WeakReference aa = new WeakReference ( a , queue ) ; a = null ; Runtime . getRuntime ( ) . gc ( ) ; System . runFinalization ( ) ; try { //TOOD线程睡觉(由于本博客采用百度安全防护,贴现场睡觉的代码不让通过) } catch ( InterruptedException e ) { e . printStackTrace ( ) ; } Reference poll = null ; while ( ( poll = queue . poll ( ) ) != null ) { System . out . println ( poll . toString ( ) ) ; } |
当垃圾回收器回收对象的时候,aa
这个弱引用将会入队进入ReferenceQueue
,所以queue.poll()
将不会为空,除非这个对象没有被垃圾回收器清理。
执行这段代码,你将会看到一句打印。类似于:java.lang.ref.WeakReference@2352544e
RefWatcher工作原理
如果将上述代码第四行注释掉,会得到什么结果呢?答案肯定是没有任何输出,因为,如果a!=null
那么就存在强引用指向a对象,垃圾回收器自然不会回收它,也就不会将aa
这个弱引用入队。这就是RefWatcher的核心工作原理。
RefWatcher核心代码
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 | void ensureGone ( KeyedWeakReference reference , long watchStartNanoTime ) { long gcStartNanoTime = System . nanoTime ( ) ; long watchDurationMs = NANOSECONDS . toMillis ( gcStartNanoTime – watchStartNanoTime ) ; removeWeaklyReachableReferences ( ) ; //移除弱引用 if ( gone ( reference ) || debuggerControl . isDebuggerAttached ( ) ) { //如果引用已经不存在了,或者处于debug调试中则返回 return ; } gcTrigger . runGc ( ) ; //触发GC removeWeaklyReachableReferences ( ) ; //再次移除弱引用,二次确认 if ( ! gone ( reference ) ) { //如果GC之后引用还是存在,那么就进行深入分析 long startDumpHeap = System . nanoTime ( ) ; long gcDurationMs = NANOSECONDS . toMillis ( startDumpHeap – gcStartNanoTime ) ; File heapDumpFile = heapDumper . dumpHeap ( ) ; //dump内存 if ( heapDumpFile == null ) { // Could not dump the heap, abort. return ; } long heapDumpDurationMs = NANOSECONDS . toMillis ( System . nanoTime ( ) – startDumpHeap ) ; heapdumpListener . analyze ( new HeapDump ( heapDumpFile , reference . key , //分析Hprof文件 reference . name , excludedRefs , watchDurationMs , gcDurationMs , heapDumpDurationMs ) ) ; } } |
代码已经注释了,只要你了解了WeakReference的原理,其实非常简单。这里就不详细分析了。剩下的工作就是交给HeapAnalyzer分析对象的最短强引用路径了。这将在下一篇文章中进行分析。
RefWatcher工作流程
- RefWatcher.watch() 创建一个 KeyedWeakReference 到要被监控的对象。
- 然后在后台线程检查引用是否被清除,如果没有,调用GC。
- 如果引用还是未被清除,把 heap 内存 dump 到 文件系统中的一个 .hprof 文件中。
- 在另外一个进程中,HeapAnalyzerService 通过 HeapAnalyzer 使用HAHA 解析这个文件。
- 得益于唯一的 reference key, HeapAnalyzer 找到 KeyedWeakReference,定位内存泄漏。
- HeapAnalyzer 计算 到 GC roots 的最短强引用路径,并确定是否泄漏。如果是,建立导致泄漏的引用链。
- 引用链传递到 APP 进程中的 DisplayLeakService, 并以通知的形式展示出来。
总结
本篇文章虽然是讲RefWatcher的工作原理,其实重点讲解了WeakReference。如果你对Java对象的引用类型还不了解,那么请一定要去学习充电了。
copyright@黑月神话,转载请注明出处:
vjson.com 本条目发布于
2015年12月30日。属于
Android高级分类。
作者是heisedeyueya。