51.LeakCanary分析-1.x-2.x

1.x版本

使用

debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3'

public class MyApplication extends Application {
      @Override
      public void onCreate() {
             super.onCreate();
             LeakCanary.install(this)
      }
}

源碼

1.LeakCanary是怎麼做到install之後自動監測Activity的?

調用install後,會註冊一個Activity生命週期的回調監聽,registerActivityLifecycleCallbacks,來綁定Activity生命週期,在Activity執行onDestroy時,調用watch方法開始檢測當前頁面是否存在內存泄漏,並分析結果(如果想要在非Activity如Fragment檢測是否存在內存泄漏,需要手動添加,調用watch方法)

2.如何判定Activity是否發生了內存泄漏

KeyedWeakReference與ReferenceQueue聯合使用,將onDestroy的Activity對象包裹成一個弱引用對象(KeyedWeakReference),開啓異步線程,在弱引用關聯的對象被回收後,會將引用添加到ReferenceQueue;通過這一點特性來判定對象是否被回收;先判斷一次,如果對象未回收舊手動觸發GC, 然後再次判斷,採用雙重判定來確保當前引用是否被回收的狀態正確性;如果兩次都未回收,則確定爲泄漏對象。

3.內存泄漏軌跡是如何生成的

採用eclipse.Mat來分析泄漏詳細,從GCRoot開始逐步生成引用軌跡。

4.內存分析是在單獨的進程執行的嗎,爲什麼?

內存分析模塊是在獨立進程中執行的,這麼設計是爲了保證內存分析過程不會對App進程造成消極的影響

5.檢測是否發生泄漏是在主線程還是子線程

開啓了一個子線程檢測的

5.Leak Canary核心類及其作用

1.DisplayLeakActivity
內存泄漏的查看頁面
2.HeapAnalyzerService
內存堆分析服務, 爲了保證App進程不會因此受影響變慢&內存溢出,運行於獨立的進程
3.HeapAnalyzer
分析由RefWatcher生成的堆轉儲信息, 驗證內存泄漏是否真實存在
4.HeapDump
堆轉儲信息類,存儲堆轉儲的相關信息
5.ServiceHeapDumpListener
一個監聽,包含了開啓分析的方法
6.RefWatcher
核心類, 翻譯自官方: 檢測不可達引用(可能地),當發現不可達引用時,它會觸發
7.HeapDumper
(堆信息轉儲)
8.ActivityRefWatcher
Activity引用檢測, 包含了Activity生命週期的監聽執行與停止

2.x版本

LeakCanary 2.0及以上版本做了代碼重構,完全使用kotlin實現,同時在使用上也做了優化。以前的版本集成的時候需要我們手動在application調用接口安裝,而新版本優化後是通過ContentProvider自動實現初始化的,使開發者完全無感知,這是對開發者更加友好的一點變化。另外一點,舊版本如果要監聽fragment的回收,需要我們手動調用接口,但是新版本也實現了初始化時對fragment的監聽。

言歸正傳,不管代碼重構如何進行,監控內存泄漏的核心原理還是不變的。無論是監控Activity還是監控Fragment,首先都是要在對象銷燬的時候拿到這個對象,那麼兩者都是怎麼拿的?

Activity:

通過application.registerActivityLifecycleCallbacks監聽Activity生命週期,在onDestroy回調中拿到Activity應用,這個方法用的很多了。

Fragment:
針對不同版本(Android O 8.0以上或以下),不同類型的fragment(android x或者support類型),雖然後處理細節有些不同,但都是通過fragmentManager監聽fragment生命週期,fragmentManager.registerFragmentLifecycleCallbacks,也就可以得到生命週期的回調,在onFragmentViewDestroyed中可以監聽fragment中view的回收,在onFragmentDestroyed監聽fragment對象的回收。

得到Activity或者Fragment的對象後,處理邏輯是一致的,接下來進入LeakCanary最核心的原理部分。

核心原理

1.當Activity或者Fragment銷燬後,獲取到他們的引用,將引用封裝到一個弱引用類型的對象中,並關聯引用隊列,利用弱引用的特性:WeakReference引用的對象如果被Gc回收,那麼這個弱引用會被加入引用隊列中。
2.封裝成弱引用後,等待5s(默認),去檢查弱引用有沒有被加入引用隊列(細節上用了一個巧妙的方式),如果加入了,說明被回收了,沒有發生內存泄漏;如果沒有加入,就可能有內存泄漏要發生。這時會手動觸發一次Gc,然後再用同樣的方式檢查引用隊列,如果被回收了,結束。如果仍然沒有被回收,那就要通過Debug.dumpHprofData進行堆轉儲並寫入到文件,啓動一個服務分析heap dump文件,找到Gc Roots引用路徑展現給用戶。

細節上用了一個巧妙的方式: 藉助一個map來同時存儲弱引用KeyedWeakReference,相當於ReferenceQueue和map兩個數據結構都存儲了這個KeyedWeakReference。當要判斷是否發生泄漏時,遍歷清空ReferenceQueue,同時把ReferenceQueue清掉的對象從map中也移除,如果對象被回收,弱引用會被加入到ReferenceQueue,那麼也就是被回收的都被清理了,剩下的都是沒有被清理的。這個時候再判斷map中是否有這個引用,如果沒有,說明沒有發生泄漏,如果還在,說明它對應的引用沒有被加入ReferenceQueue,也就是發生了泄漏。

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