Android开发者,是时候了解LeakCanary了( 二 )


比如我们经常看到这种用法
还有也有这样一种用法
这样就可以把对象和ReferenceQueue关联起来,进行对象是否gc的判断了 。另外我们从弱引用的特征中看到,弱引用是不会影响到这个对象是否被gc的,很适合用来监控对象的gc情况 。
2.GC
java中有两种手动调用GC的方式 。
2.2 监控
我们在第一节中提到,Activity和Fragment都依赖于响应的LifecycleCallback来回调销毁信息,然后调用了ObjectWatcher.watch添加了销毁后的监控 。接下来我们看ObjectWatcher.watch做了什么操作 。
这里我们看到,有一个存储着KeyedWeakReference的ReferenceQueue对象 。在每次增加watch object的时候,都会去把已经处于ReferenceQueue中的对象给从监控对象的map即watchObjects中清理掉,因为这些对象都已经被回收了 。然后再去生成一个KeyedWeakReference,这个对象就是一个持有了key和监测开始时间的WeakReference对象 。最后再去调用moveToRetained,相当于记录和回调给监控方这个对象正式开始监测的时间 。
那么我们现在已经拿到了需要监控的对象了,但是又是怎么去判断这个对象已经内存泄露的呢?这就要继续往下面看 。我们主要到前面在讲解InternalAppWatcher的install方法的时候,除了install了Activity和Fragment的检测器,还调用了onAppWatcherInstalled(application)方法,看代码发现这个方法就是InternalLeakCanary的invoke方法 。
我们看到首先是初始化了heapDumper,gcTrigger,heapDumpTrigger等对象用于gc和heapDump,同时还实现了OnObjectRetainedListener,并把自己添加到了上面的onObjectRetainedListeners中,以便每个对象moveToRetained的时候,InternalLeakCanary都能获取到onObjectRetained()的回调,回调里就只是回调了heapDumpTrigger.onObjectRetained()方法 。看来都是依赖于HeapDumpTrigger这个类 。
HeapDumpTrigger主要的处理逻辑都在checkRetainedObjects方法中 。
那么HeapDumpTrigger具体做了些啥呢?我理了一下主要是下面几个功能:

  • 后台线程轮询当前还存活着的对象
  • 如果存活的对象大于0,那就触发一次GC操作,回收掉没有泄露的对象
  • GC完后,仍然存活着的对象数和预定的对象数相比较,如果多了就调用heapDumper.dumpHeap()方法把对象dump成文件,并交给HeapAnalyzerService去分析
  • 根据存活情况展示通知
2.3 总结
看到了这里,我们应该脑海中有概念了 。Activity和Fragment通过注册系统的监听在onDestroy的时候把自己的引用放入ObjectWatcher进行监测,监测主要是通过HeapDumpTrigger类轮询进行,主要是调用AndroidHeapDumper来dump出文件来,然后依赖于HeapAnalyzerService来进行分析 。后面一小节,我们将会聚焦于对象dump操作和HeapAnalyzerService的分析过程 。
3. dump对象及分析
3.1 dump对象
hprof是JDK提供的一种JVM TI Agent native工具 。JVM TI,全拼是JVM Tool interface,是JVM提供的一套标准的C/C++编程接口,是实现Debugger、Profiler、Monitor、Thread Analyser等工具的统一基础,在主流Java虚拟机中都有实现 。hprof工具事实上也是实现了这套接口,可以认为是一套简单的profiler agent工具 。我们在新知周推:10.8-10.14(启动篇)中也提到过,可以参考其中美团的文章 。
用过Android Studio Profiler工具的同学对hprof文件都不会陌生,当我们使用Memory Profiler工具的Dump Java heap图标的时候,profiler工具就会去捕获你的内存分配情况 。但是捕获以后,只有在Memory Profiler正在运行的时候我们才能查看,那么我们要怎么样去保存当时的内存使用情况呢,又或者我想用别的工具来分析堆分配情况呢,这时候hprof文件就派上用场了 。Android Studio可以把这些对象给export到hprof文件中去 。
LeakCanary也是使用的hprof文件进行对象存储 。hprof文件比较简单,整体按照 前置信息 + 记录表的格式来组织的 。但是记录的种类相当之多 。具体种类可以查看HPROF Agent 。
同时,android中也提供了一个简便的方法Debug.dumphprofData(filePath)可以把对象dump到指定路径下的hprof文件中 。LeakCanary使用使用Shark库来解析Hprof文件中的各种record,比较高效,使用Shark中的HprofReader和HprofWriter来进行读写解析,获取我们需要的信息 。大家可以关注一些比较重要的,比如:
  • Class Dump
  • Instance Dump
  • Object Array Dump
  • Primitive Array Dump
dump具体的代码在AndroidHeapDumper类中 。HprofReader和HprofWriter过于复杂,有兴趣的直接查看源码吧
3.2 对象分析
前面我们已经分析到了,HeapDumpTrigger主要是依赖于HeapAnalyzerService进行分析 。那么这个HeapAnalyzerService究竟有什么玄机?让我们继续往下面看 。可以看到HeapAnalyzerService其实是一个ForegroundService 。在接收到分析的Intent后就会调用HeapAnalyzer的analyze方法 。所以最终进行分析的地方就是HeapAnalyzer的analyze方法 。


推荐阅读