記憶體分析工具 MAT 的使用
1 記憶體洩漏的排查方法
Dalvik Debug Monitor Server (DDMS) 是 ADT外掛的一部分,其中有兩項功能可用於記憶體檢查 :
· heap 檢視堆的分配情況
· allocation tracker跟蹤記憶體分配情況
DDMS 這兩項功能有助於找到記憶體洩漏的操作行為。
Eclipse Memory Analysis Tools (MAT) 是一個分析 Java堆資料的專業工具,用它可以定位記憶體洩漏的原因。
1.1 觀察 Heap
· 執行程式,然後進入 DDMS管理介面,如下:
PS : 點選工具欄上的 來更新統計資訊
點選右側的Cause GC按鈕或工具欄上的 即可檢視當前的堆情況,如下:
主要關注兩項資料:
o Heap Size 堆的大小,當資源增加,當前堆的空餘空間不夠時,系統會增加堆的大小,若超過上限 (例如 64M,視平臺和具體機型而定)則會被殺掉
o Allocated 堆中已分配的大小,這是應用程式實際佔用的記憶體大小,資源回收後,此項資料會變小
· 檢視操作前後的堆資料,看是否有記憶體洩漏對單一操作(比如新增頁,刪除頁)進行反覆操作,如果堆的大小一直增加,則有記憶體洩漏的隱患。
1.2 利用MAT分析記憶體堆
DDMS 可以將當前的記憶體 Dump成一個
· 獲取 hprof檔案點選工具欄上的 按鈕,將記憶體資訊儲存成檔案。如果是用 MAT Eclipse 外掛獲取的 Dump檔案,則不需要經過轉換,Adt會自動進行轉換然後開啟。
· 轉換 hprof檔案
DDMS Dump 出的檔案要經過轉換才能被 MAT識別,Android SDK提供了這個工具hprof-conv(位於
sdk/tools下)
· ./hprof-conv xxx-a.hprof xxx-b.hprof
·
1.3 Histogram 查詢
用的最多的功能是Histogram,點選 Actions下的 Histogram項將得到 Histogram結果:
它按類名將所有的例項物件列出來,可以點選表頭進行排序,在表的第一行可以輸入正則表示式來匹配結果 :
在某一項上右鍵開啟選單選擇list objects ->with incoming refs將列出該類的例項:
它展示了物件間的引用關係,比如展開後的第一個子項表示這個 HomePage(0x420ca5b0)被 HomePageContainer(0x420c9e40)中的 mHomePage屬性所引用.
快速找出某個例項沒被釋放的原因,可以右健Path to GC Roots-->exclue all phantom/weak/soft etc. reference:
得到的結果是:
從表中可以看出 PreferenceManager -> … ->HomePage這條線路就引用著這個 HomePage例項。用這個方法可以快速找到某個物件的GC Root,一個存在 GC Root的物件是不會被 GC回收掉的.
1.4 Histogram 對比
為查詢記憶體洩漏,通常需要兩個 Dump結果作對比,開啟 Navigator History面板,將兩個表的 Histogram結果都新增到 Compare Basket中去 :
新增好後,開啟 Compare Basket面板,得到結果:
點選右上角的!按鈕,將得到比對結果:
注意,上面這個對比結果不利於查詢差異,可以調整對比選項:
再把對比的結果排序,就可得到直觀的對比結果:
也可以對比兩個物件集合,方法與此類似,都是將兩個 Dump結果中的物件集合新增到Compare Basket中去對比。找出差異後用 Histogram查詢的方法找出 GC Root,定位到具體的某個物件上。
1.5 例子
舉例一個典型的分析記憶體洩漏的過程:
1. 使用 Heap檢視當前堆大小為 23.00M
2. 新增一個頁後堆大小變為 23.40M
3. 將新增的一個頁刪除,堆大小為 23.40M
4. 多次操作,結果仍相似,說明新增/刪除頁存在記憶體洩漏 (也應注意排除其它因素的影響)
5. Dump 出操作前後的 hprof 檔案 (1.hprof,2.hprof),用 mat開啟,並得到 histgram結果
6. 使用 HomePage欄位過濾 histgram結果,並列出該類的物件例項列表,看到兩個表中的物件集合大小不同,操作後比操作前多出一個 HomePage,說明確實存在洩漏
7. 將兩個列表進行對比,找出多出的一個物件,用查詢 GC Root的方法找出是誰串起了這條引用線路,定位結束
PS :
· 很多時候堆增大是 Bitmap引起的,Bitmap在 Histogram中的型別是 byte [],對比兩個 Histogram中的 byte[] 物件就可以找出哪些 Bitmap有差異
· 多使用排序功能,對找出差異很有用
2 記憶體洩漏的原因分析
總結出來只有一條:存在無效的引用!良好的模組設計以及合理使用設計模式有助於解決此問題。
3 Tips
· 使用 android:largeHeap="true"標記 (API Level >= 11)在 AndroidManifest.xml中的 Application節點中宣告即可分配到更大的堆記憶體, android:largeHeap標記在 Android系統應用中也有廣泛的應用 ,比如 Launcher, Browser這些記憶體大戶上均有使用.
4 參考
同事起草,整理分享,可以轉載收錄。