性能優化專題十二--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,然後做第二次判斷,如果還是不存在,則表明出現了內存泄漏。

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