性能优化专题十二--LeakCanary简析

简介

LeakCanary是一款开源的内存泄漏检查工具,在项目中,可以使用它来检测Activity是否能够被GC及时回收。github的地址为https://github.com/square/leakcanary

LeakCanary的核心原理是基于WeakReference和ReferenceQueue进行检测。WeakReference的构造函数可以传入ReferenceQueue,当WeakReference指向的对象被垃圾回收时,会把WeakReference放入ReferenceQueue。调用ReferenceQueue.poll()可以把WeakReference获取出来。

LeakCanary实现简述

1、LeakCanary.install()返回了一个RefWatcher。RefWatcher.queue是一个ReferenceQueue实例。

2、通过继承自WeakReference的KeyedWeakReference,增加了key+name字段从而支持对象的唯一起名(通过UUID)和名字获取。

3、在RefWatcher.watch()时把UUID放入RefWatcher.retainedKeys,并把对象跟RefWatcher.queue关联。

4、通过Runtime.gc()和System.runFinalization()进行GC操作。

5、在RefWatcher.removeWeaklyReachableReferences()调用queue.poll()取出KeyedWeakReference,并从retainedKeys中删除。所以,如果retainedKeys中key仍存在,说明对象未被垃圾回收。反之则已经垃圾回收。

6、如果对象未被垃圾回收,则执行分析。

使用方式解析

将LeakCanary引入AS,在Application中调用如下方法,可以跟踪Activity是否被GC回收。

Install方法如下:

其中listenerServiceClass方法传入了展示分析结果的Service(DisplayLeakService);excludedRefs方法排除了开发中可以忽略的泄漏路径;buildAndInstall是主要的函数,实现了activity是否能被释放的监听。

buildAndInstall核心有两处:

build方法返回了RefWatcher对象,里面存储的是弱引用队列中未被回收的对象的引用;

其中的ActivityRefWater.install方法就是将Activity注册到这个watcher监控队列中:

通过registerActivityLifecycleCallbacks来监听Activity的生命周期:

lifecycleCallbacks监听Activity的onDestroy方法,正常情况下activity在onDestroy后需要立即被回收,onActivityDestroyed方法最终会调用RefWatcher.watch方法:

这里引出了第一个知识点,弱引用和引用队列ReferenceQueue联合使用时,如果弱引用持有的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。即 KeyedWeakReference持有的Activity对象如果被垃圾回收,该对象就会加入到引用队列queue监测机制利用了Java的WeakReference和ReferenceQueue,通过将Activity包装到WeakReference中,被WeakReference包装过的Activity对象如果被回收,该WeakReference引用会被放到ReferenceQueue中,通过监测ReferenceQueue里面的内容就能检查到Activity是否能够被回收。检查方法如下:

onDestroy以后,一旦主线程空闲下来,延时5秒执行一个任务:先判断Activity有没有被回收?如果已经回收了,说明没有内存泄漏,如果还没回收,我们进一步确认,手动触发一下gc,然后再判断有没有回收,如果这次还没回收,说明Activity确实泄漏了,接下来把泄漏的信息展示给开发者就好了。

1、  首先通过removeWeaklyReachablereference来移除已经被回收的Activity引用

2、 通过gone(reference)判断当前弱引用对应的Activity是否已经被回收,如果已经回收说明activity能够被GC,直接返回即可。

3、  如果Activity没有被回收,调用GcTigger.runGc方法运行GC,GC完成后在运行第1步,然后运行第2步判断Activity是否被回收了,如果这时候还没有被回收,那就说明Activity可能已经泄露。

4、  如果Activity泄露了,就抓取内存dump文件(Debug.dumpHprofData)

5、  之后通过HeapAnalyzerService.runAnalysis进行分析内存文件分析

6、获取到heap文件后,通知ServiceHeapDumpListener进行分析heapdumpListener.analyze(heapDump);

7、接着通过HeapAnalyzer(checkForLeak—findLeakingReference---findLeakTrace)来进行内存泄漏分析。

8、  最后通过DisplayLeakService进行内存泄漏的展示。

9、 在堆转储中搜索具有相应键的{@link KeyedWeakReference}实例,然后计算从该实例到GC根的最短强引用路径

总结

LeakCanary实现内存泄漏的主要判断逻辑是这样的。当我们观察的Activity或者Fragment销毁时,我们会使用一个弱引用去包装当前销毁的Activity或者Fragment,并且将它与本地的一个ReferenceQueue队列关联。我们知道如果GC触发了,系统会将当前的引用对象存入队列中。
如果没有被回收,队列中则没有当前的引用对象。所以LeakCanary会去判断,ReferenceQueue是否有当前观察的Activity或者Fragment的引用对象,第一次判断如果不存在,就去手动触发一次GC,然后做第二次判断,如果还是不存在,则表明出现了内存泄漏。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章