Android內存泄漏研究
概念
根搜索算法
Android 虛擬機的垃圾回收采用的是根搜索算法。GC 會從根節點(GC Roots)開始對 heap 進行遍歷。到最后,部分沒有直接或者間接引用到 GC Roots 的就是需要回收的垃圾,會被 GC 回收掉。
根搜索算法相比引用計數法很好的解決了循環引用的問題。舉個例子,Activity 有 View 的引用,View 也有 Activity 的引用,之前我還嘗試去源代碼里找 Activity 何時和 View 斷開連接是大錯特錯了。當 Activity finish 掉之后,Activity 和 View 的循環引用已成孤島,不再引用到 GC Roots,無需斷開也會被回收掉。
內存泄漏
Android 內存泄漏指的是進程中某些對象(垃圾對象)已經沒有使用價值了,但是它們卻可以直接或間接地引用到 gc roots 導致無法被 GC 回收。無用的對象占據著內存空間,使得實際可使用內存變小,形象地說法就是內存泄漏了。
場景
-
類的靜態變量持有大數據對象
靜態變量長期維持到大數據對象的引用,阻止垃圾回收。 -
非靜態內部類的靜態實例
非靜態內部類會維持一個到外部類實例的引用,如果非靜態內部類的實例是靜態的,就會間接長期維持著外部類的引用,阻止被回收掉。 -
資源對象未關閉
資源性對象如 Cursor、File、Socket,應該在使用后及時關閉。未在 finally 中關閉,會導致異常情況下資源對象未被釋放的隱患。 -
注冊對象未反注冊
未反注冊會導致觀察者列表里維持著對象的引用,阻止垃圾回收。 -
Handler臨時性內存泄露
Handler 通過發送 Message 與主線程交互,Message 發出之后是存儲在 MessageQueue 中的,有些 Message 也不是馬上就被處理的。在 Message 中存在一個 target,是 Handler 的一個引用,如果 Message 在 Queue 中存在的時間越長,就會導致 Handler 無法被回收。如果 Handler 是非靜態的,則會導致 Activity 或者 Service 不會被回收。
由于 AsyncTask 內部也是 Handler 機制,同樣存在內存泄漏的風險。
此種內存泄露,一般是臨時性的。
預防
- 不要維持到 Activity 的長久引用,對 activity 的引用應該和 activity 本身有相同的生命周期。
- 盡量使用context-application代替context-activity
- Activity 中盡量不要使用非靜態內部類,可以使用靜態內部類和WeakReference代替。
檢測
靜態檢測
靜態檢測主要是檢測資源未關閉的情況,Eclipse 和 Android Studio 都可以檢測出 IO 或者 Socket 未關閉的情況,然后在 finally 中關閉即可。
動態監測
動態檢測主要是依靠 MAT 這個工具。2011 年 Google IO 有一個主題演講,非常詳細地講解了內存泄露的檢測,包含 MAT 工具的使用,值得一看。我在某項目中使用 MAT 檢測,發現一處內存泄漏,分享一下過程。從首頁到商戶列表到商戶詳情再退回首頁執行Dump HPROF File,查看 MAT 中的Histogram,過濾 Activity 后,結果如下:
Histogram
仍然存在ShopInfoActivity的實例,選中右鍵點擊Merge Shortest Paths to GC Roots,結果如下:
此處輸入圖片的描述
可以看到ShopDatabase中維持著ShopInfoActivity的引用,查看源代碼如下:
public class ShopDatabase { … private static ShopDatabase instance; public static ShopDatabase getInstance (Context context) { if (instance == null && context != null) { instance = new ShopDatabase (context); } return instance; } protected Context context; … }
很明顯,靜態變量instance長期持有context的引用,造成內存泄露。
所以動態檢測內存泄露的一個簡單思路就是隨意操作 APP,最后返回首頁,然后用 MAT 檢測,查看是否存在 Activity 多于一個或者 Activity 不正常存在的問題。
參考資料
- Avoiding memory leaks
- Memory Analysis for Android Applications
- memory_management_for_android_apps
- Google IO:Android 內存管理主題演講記錄
- Android 內存泄漏分析及調試