LeakCanary源码分析第二讲-RefWatcher详解

LeakCanary源码分析第二讲-RefWatcher详解

如果你已经阅读了LeakCanary源码分析第一讲,那么LeakCanary的基本架构应该已经掌握了。本文将详细分析RefWatcher的工作原理,当RefWatcher检查到引用路径不是弱通路的时候就会触发HeapDumper

WeakReference和ReferenceQueue

要理解RefWatcher的工作原理,首先需要知道WeakReference。当GC线程扫描它所管辖的内存区域时,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否(这一点与SoftReference不同),都会回收它的内存。由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。《LeakCanary源码分析第二讲-RefWatcher详解》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, 并以通知的形式展示出来。

《LeakCanary源码分析第二讲-RefWatcher详解》

总结

本篇文章虽然是讲RefWatcher的工作原理,其实重点讲解了WeakReference。如果你对Java对象的引用类型还不了解,那么请一定要去学习充电了。






  copyright@黑月神话,转载请注明出处:
vjson.com 本条目发布于
2015年12月30日。属于
Android高级分类。
作者是heisedeyueya

    原文作者:小企鹅听雨Candice
    原文地址: https://blog.csdn.net/xiaoqietingyu/article/details/51815993
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞