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 各個模塊如何配合達到檢測目的。
泳道圖
來自:http://www.jianshu.com/p/3f1a1cc1e964