LeakCanary的使用和實現原理
-
Android 應用記憶體洩漏問題,一直是效能優化的重點。在不清楚記憶體洩漏的大致範圍時,通過人為測試模擬重現或無目的地分析 heap dump等方法來檢測,都太繁瑣、耗時且定位不準。
什麼是記憶體洩漏?
在 Java 世界中,一切物件都有生命週期,如同人的壽命。人死燈滅,入輪迴,轉世投胎。Java 物件的生命週期結束後,將被 GC 回收,原先佔用的記憶體會有新的用途。但凡是總有例外,就如孫悟空可以修改生死譜長生不死,聶小倩能殘存人間人鬼相戀。Java 物件有時也會”長死不死“,GC 拿它沒有辦法,這種情況就是記憶體洩漏。造成這種情況的原因是:Java 物件被另一個生命週期更長物件持有,具有 可達性
問:有沒有一種簡單直接且能有效定位記憶體洩漏位置的方法呢?
答:有,那就是 LeakCanary 。我們可以簡單地人為:將一個 App 作為輸入,通過 LeakCanary 檢測後,就會得到記憶體洩漏位置結果(如果存在的話)。
LeakCanary
知其然知其所以然,LeakCanary 如此強大實用,那麼:LeakCanary 是怎麼實現的?
- Android 應用的整個生命週期由其元件的生命週期組成,如下圖中所示。使用者使用應用的過程中,在不同介面之間跳轉,每個介面都經歷著”生死“的轉換,可在此建立檢測點。
Activity
/Fragment
onDestory()
回撥方法, 進入此方法後,Activity
/Fragment
生命週期結束,應該被回收。 -
簡述宣告週期
-
然後我們需要解決:如何得到未被回收的物件。
ReferenceQueue
+WeakReference
+手動呼叫 GC
可實現這個需求。WeakReference 建立時,傳入一個 ReferenceQueue 物件。當被 WeakReference 引用的物件的生命週期結束,一旦被 GC 檢查到,GC 將會把該物件新增到 ReferenceQueue 中,待ReferenceQueue處理。當 GC 過後物件一直不被加入 ReferenceQueue,它可能存在記憶體洩漏。
-
獲得未被回收的 Object
-
找到了未被回收的物件,如何確認是否真的記憶體洩漏?這裡可以將問題轉換為:未被回收的物件,是否被其他物件引用?找出其最短引用鏈。
VMDebug
+HAHA
完成需求。VM 會有堆內各個物件的引用情況,並能以
hprof
檔案匯出。HAHA 是一個由 square 開源的 Android 堆分析庫,分析hprof
檔案生成Snapshot
物件。Snapshot
用以查詢物件的最短引用鏈。 -
解析hprof
- 找到最短引用鏈後,定位問題,排查程式碼將會事半功倍。
-
如下泳道圖分析, LeakCanary 各個模組如何配合達到檢測目的。
泳道圖