內存泄露分析之MAT工具使用

轉載請註明地址:http://blog.csdn.net/yincheng886337/article/details/50524890

MAT工具使用


理解相關概念

在瞭解MAT工具之前,我們需先對以下幾個概念有所認知:

1)強引用、弱引用、軟引用、虛引用

2)Shallow Size、Retained Size、Heap Size和Allocated

MAT工具實戰

看完1)、2)兩篇博客,相信大家此時對幾個概念已具備了一定認知,下面就進入正題MAT工具的使用,說到MAT工具(Memory Analyzer Tool),首先是工具的獲取與安裝。

MAT工具獲取路徑:http://download.eclipse.org/mat/1.5/update-site/

MAT安裝步驟詳情請自行百度

安裝好工具後,就需要去獲取hprof文件,如果獨立版的MAT,需採用hprof-conv <源文件> <目標文件> 命名進行轉換。

下面以Android Studio dump操作示例:

步驟1:

 

圖1.抓取hprof文件

步驟2:

 

圖2.轉換hprof文件

 

抓好hprof文件後,採用MAT打開hprof文件後,看到的界面如下圖:

 

圖3.MAT的overview圖

 

有了MAT和hprof文件,且功能正常,馬上進入MAT分析內存階段。採用MAT進行內存分析主要分析以下幾點:

1.程序是否內存泄露;

2.程序中大對象的佔用是否合理;

3.程序中部分對象產生內存是否可以優化;

有了分析的目標,我們就開始朝着目標開始分析:

方法1:根據Leak Suspects快速查看泄露的可疑點

Step1:進入Leak Suspects頁面(如圖4)

 

圖4.Leak Suspect圖

Step2:進入Leak Suspects的詳情頁面(如圖5)

 

圖5.Leak Suspect詳情頁面

Step3:查看泄露可疑對象(如圖6)

  

圖6.泄露可疑對象

Step4:查看泄露可疑對象被誰引用(如圖7)


 圖7.泄露可疑對象引用關係

Step5:可疑對象被確認爲一張圖片後,就查看圖片的相關屬性並做相應記錄(如圖8)

 

圖8.查看可疑對象屬性 

Step6:將可疑對象保存至具體位置,便於查看圖片真相,注意文件保存一定要以.data爲後綴(如圖9)

  

圖9.保存圖片 

Step7:採用GIMP軟件打開剛保存的文件,修改圖片類型以及寬和高(寬高和Step5中屬性一致)(如圖10)

 

圖10.GIMP打開圖片

 

至此我們就可以確定該可疑對象是哪張圖片,然後找到相應的代碼,查看爲何該圖片沒有釋放,爲何它這麼大,是否可以優化?

方法1優化建議:

1.如果是切圖問題或是矢量圖,可以從圖片上進行優化,如製作成.9圖抑或自己用xml做drawable圖;

2.如果使用完沒有釋放,抑或還沒有顯示就已經加載,可以考慮採用ViewStub來加載,但ViewStub不能與merge共同使用,否則會報錯。

 

方法2:利用Top Consumers或Dominator Tree查看大對象(如圖11-12)

 圖11. 利用Top Consumers查看大對象 

 

圖12 利用Dominator Tree查看大對象

由於此種方法比較直觀,在此就不贅述,如有不懂請自行查閱其他文獻

方法2優化建議:

當面對大對象時,如果是集合類存儲的對象,可以考慮使用下Android提供的ArrayMap以及SparseArray來替換HashMap;

 

方法3:利用Histogram和Dominator Tree分析內存泄露

在分析內存泄露時,必須要掌握粒度,所謂粒度就是你此刻dump的hprof文件究竟是分析誰的泄露,如果你在開始前心中沒有個目標,最後取出來的hprof也分析不出什麼原因。粒度越小,對你分析問題也就越有利,當你把一個個小粒度問題解決後,整個App的泄露就迎刃而解了。也許這麼說,大家心中有點迷糊。下面就舉例來說吧:

假如現在有個項目包含Module幾十個,每個Module包含的Activity數以百計,現在讓你分析它是否內存泄露,如果你只是胡亂抓個hprof根本分析不出什麼。假如你就針對某個Activity分析這樣問題就簡單多了。比如你現在分析ActivityA的內存泄露問題,你可以參考如下步驟:

Step1:進入ActivityA之前,你先dump個hprof文件HprofA;

Step2:進入ActivityA操作一會,再退出ActivityA後dump個hprof文件HprofB;

Step3:採用Histogram和Dominator Tree對比分析這兩個Hprof文件,即可得出ActivityA是否泄露

現在以分析TestActivity爲例,按上述步驟實戰分析,先抓取進入TestActivity前後的hprof文件,按如下步驟對比兩個hprof的異同(如圖13-14):

 

圖13 選擇所需比較的hprof


圖14 比較兩個hprof

正如圖14所示,易知在執行進出TestActivity後,多出了個TestActivity對象,按理論上來說在進入Activity後會創建個Activity,但是按Back鍵返回後這個Activity就會被銷燬進而從Task棧上被移除,也就是說這個操作前後不應該會多出個Activity,因此可以斷定TestActivity存在泄漏。

TestActivity存在泄漏,那我們應該怎麼解決呢?因此我們就需要找到爲何泄漏,爲什麼本該銷燬的Activity卻沒有被銷燬?如知真相如何,請看下圖15-16


圖15 獲取TestActivity的Reference chain


圖16TestActivity的引用關係

從圖16易知TestActivity沒有被釋放就是因爲GC Root(TestActivity$1)引用着TestActivity,到此原因也一目瞭然。找到了只是開始,解決纔是關鍵。這時讓我們查看下TestActivity代碼:

public class TestActivity extends Activity {   
    private static final Object mLock = new Object(); 
    @Override
    protected void onCreate(Bundle savedInstanceState) {        
        super.onCreate(savedInstanceState);
        DebugUtil.StrictModeDebug();
        setContentView(R.layout.test_main);   
        new Thread(){//匿名線程
            public void run() {
                synchronized (mLock) {
                    try {
                        mLock.wait();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        }.start();
    }
}

從代碼上可以發現TestActivity裏存在個匿名線程,且一直處於等待狀態,直到退出TestActivity仍未被喚醒,進而導致該線程就一直沒有結束,它所持有的TestActivity也就無法被釋放了(可能大家聽到此處會很疑惑,線程沒有結束可以理解,但是它並沒有持有TestActivity呀?我只能說是隱含this,如還不明白,請自行參閱java內部類相關內容),如要解決此泄露,只需在ActivityonDestory裏將線程喚醒讓其可以正常結束就OK了。

方法3優化建議:

1.使用線程時,一定要確保線程在週期性對象(如Activity)銷燬時能正常結束,如能正常結束,但是Activity銷燬後還需執行一段時間,也可能造成泄露,此時可採用WeakReference方法來解決,另外在使用Handler的時候,如存在Delay操作,也可以採用WeakReference;

2.使用Handler + HandlerThread時,記住在週期性對象銷燬時調用looper.quit()方法;

3.建議少使用匿名類或內部類,可考慮使用嵌套類(帶static那種類),減少對週期性對象的隱性持有;

至此,MAT使用也告一段落,但其功能遠不止於此,如需瞭解更多可參閱如下網址:

1.http://ju.outofmemory.cn/entry/129445

2.http://developer.android.com/intl/zh-cn/training/improving-layouts/smooth-scrolling.html#ViewHolder

3.http://help.eclipse.org/luna/index.jsp?topic=/org.eclipse.mat.ui.help/welcome.html





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章