1 內存泄漏的排查方法
Dalvik Debug Monitor Server (DDMS) 是 ADT插件的一部分,當中有兩項功能可用於內存檢查 :
· heap 查看堆的分配情況
· allocation tracker跟蹤內存分配情況
DDMS 這兩項功能有助於找到內存泄漏的操作行為。
Eclipse Memory Analysis Tools (MAT) 是一個分析 Java堆數據的專業工具。用它能夠定位內存泄漏的原因。
工具地址 : http://www.eclipse.org/mat/downloads.php
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這些內存大戶上均有使用.
