1 記憶體洩漏的排查方法

Dalvik Debug Monitor Server (DDMS) 是 ADT外掛的一部分,當中有兩項功能可用於記憶體檢查 :

·    heap
檢視堆的分配情況

·    allocation tracker跟蹤記憶體分配情況

DDMS 這兩項功能有助於找到記憶體洩漏的操作行為。

Eclipse Memory Analysis Tools (MAT) 是一個分析 Java堆資料的專業工具,用它能夠定位記憶體洩漏的原因。

工具地址 : https://www.eclipse.org/mat/

1.1 觀察 Heap

·        執行程式,然後進入 DDMS管理介面,例如以下:

PS : 點選工具欄上的  來更新統計資訊

點選右側的 Cause GC button或工具欄上的  就可以檢視當前的堆情況,例如以下:

主要關注兩項資料:

o    Heap Size
堆的大小,當資源新增,當前堆的空餘空間不夠時,系統會新增堆的大小,若超過上限 (比如 64M,視平臺和詳細機型而定)則會被殺掉

o    Allocated
堆中已分配的大小,這是應用程式實際佔用的記憶體大小,資源回收後,此項資料會變小

·        檢視操作前後的堆資料,看是否有記憶體洩漏 

對單一操作(比方加入頁,刪除頁)進行重複操作,假設堆的大小一直新增,則有記憶體洩漏的隱患。

1.2 利用MAT分析記憶體堆

DDMS 能夠將當前的記憶體 Dump成一個 hprof格式的檔案,MAT
讀取這個檔案後會給出方便閱讀的資訊,配合它的查詢,對照功能,就能夠定位記憶體洩漏的原因。

·        獲取 hprof檔案 

點選工具欄上的  button,將記憶體資訊儲存成檔案。 假設是用
MAT Eclipse 外掛獲取的 Dump檔案,則不須要經過轉換,Adt會自己主動進行轉換然後開啟。

·        轉換 hprof檔案 

DDMS Dump 出的檔案要經過轉換才幹被 MAT識別,Android SDK提供了這個工具 hprof-conv (位於
sdk/tools下)

·    ./hprof-conv xxx-a.hprof xxx-b.hprof

·        用 MAT開啟轉換後的 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面板,得到結果:

點選右上角的 ! button,將得到比對結果:

注意,上面這個對照結果不利於查詢差異,能夠調整對照選項:

再把對照的結果排序,就可得到直觀的對照結果:

也能夠對照兩個物件集合,方法與此類似,都是將兩個 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 參考

·    DDMS
官方教程 http://developer.android.com/tools/debugging/ddms.html

·    MAT
下載 http://www.eclipse.org/mat/downloads.php

·    MAT
使用 http://android-developers.blogspot.tw/2011/03/memory-analysis-for-android.html