上面這段代碼,對Java熟悉的同學都應該了解內部類對象持有了外部類對象引用,而leak作為靜態變量在非空判斷下只產生了一個對象,因此當旋轉屏幕時生成新的Activity的時候舊的Activity的引用依然被持有,如下圖:
通過觀察旋轉屏幕前后Log中GC的信息也能看出heap的分配往上漲了許多,并且在GC執行完heap的分配穩定之后并沒有降下來,這就是內存泄漏的跡象。
我們通過MAT來進行分析。先下載MAT-http://www.eclipse.org/mat/,可以作為Eclipse插件下載,也可以作為RCP應用下載,本質上沒有區別。DDMS中選中應用對應的進程名,點擊Dump HPROF file的按鈕,等一小段時間生成HPROF文件,如果是Eclipse插件的話,Eclipse會為這個HPROF自動轉化成標準的HPROF并自動打開MAT分析界面。如果是作為RCP應用的話,需要用sdk目錄tools中的hprof-conv工具來進行轉化,也就是上文提及的命令hprof-conv orig.hprof converted.hprof,這種方式保存HPROF文件的位置選擇更為自主,你也可以修改Eclipse的設置讓Eclipse提示保存而不是自動打開,在Preferences -> Android -> DDMS中的HPROF Action由Open in Eclipse改為Save to disk。打開MAT,選擇轉化好的HPROF文件,可以看到Overview的界面如下圖:
中間的餅狀圖就是根據我們上文所說的Retained heap的概念得到的內存中一些Retained Size最大的對象。點擊餅狀圖能看到這些對象類型,但對內存泄漏的分析還遠遠不夠。再看下方Action中有Dominator Tree和Histogram的選項,這一般來說是最有用的工具。還記得我們上文說過的Dominator Tree的概念嗎,這就是我們用來跟蹤內存泄漏的方式。點開Dominator Tree,會看到以Retained heap排序的一系列對象,如下圖:
Resources類型對象由于一般是系統用于加載資源的,所以Retained heap較大是個比較正常的情況。但我們注意到下面的Bitmap類型對象的Retained heap也很大,很有可能是由于內存泄漏造成的。所以我們右鍵點擊這行,選擇Path To GC Roots ->exclude weak references,可以看到下圖的情形:
Bitmap最終被leak引用到,這應該是一種不正常的現象,內存泄漏很可能就在這里了。MAT不會告訴哪里是內存泄漏,需要你自行分析,由于這是Demo,是我們特意造成的內存泄漏,因此比較容易就能看出來,真實的應用場景可能需要你仔細的進行分析。
根據我們上文介紹的Dominator的概念,leak對象是該Bitmap對象的Dominator,應該出現在Dominator Tree視圖里面,但實際上卻沒有。這是由于MAT并沒有對weak references做區別對待,這也是我們選擇exclude weak references的原因。如果我們Path To GC Roots ->with all references,我們可以看到下圖的情形:
可以看到還有另外一個對象在引用著這個Bitmap對象,了解weak references的同學應該知道GC是如何處理weak references,因此在內存泄漏分析的時候我們可以把weak references排除掉。
有些同學可能希望根據某種類型的對象個數來分析內存泄漏。我們在Overview視圖中選擇Actions -> Histogram,可以看到類似下圖的情形:
上圖展示了內存中各種類型的對象個數和Shallow heap,我們看到byte[]占用Shallow heap最多,那是因為Honeycomb之后Bitmap Pixel Data的內存分配在Dalvik heap中。右鍵選中byte[]數組,選擇List Objects -> with incoming references,可以看到byte[]具體的對象列表:
我們發現第二個byte[]的Retained heap較大,內存泄漏的可能性較大,因此右鍵選中這行,Path To GC Roots -> exclude weak references,同樣可以看到上文所提到的情況,我們的Bitmap對象被leak所引用到,這里存在著內存泄漏。
在Histogram視圖中第一行中輸入com.example.android.hcgallery,過濾出我們自己應用中的類型,如下圖:
我們發現本應該只有一個MainActivity現在卻有兩個,顯然不正常。右鍵選擇List Objects -> with incoming references,可以看到這兩個具體的MainActivity對象。右鍵選中Retained heap較大的MainActivity,Path To GC Roots -> exclude weak references,再一次可疑對象又指向了leak對象。
以上是MAT一些基本的用法,如果你感興趣,可以自行深入的去了解MAT的其他功能。
原文轉自:http://my.eoe.cn/futurexiong/archive/1299.html