在Eclipse中使用MAT分析Android程序內存使用情況(轉)

對於Android這種手持設備來講,一般不會帶有太大的內存,並且通常用戶都是長時間不重啓手機,因此編寫程序的時候必需要很是當心的使用內存,儘可能避免有內存泄露的問題出現。一般分析程序中潛在內存泄露的問題是一件頗有難度的工做,通常都是由團隊中的資深工程師負責,並且隨着程序代碼量的提升,難度還會逐步加大。android

今天要介紹一個在Eclipse中使用的內存分析工具——MAT(Eclipse Memory Analyzer,主頁在http://www.eclipse.org/mat/)。它是一個功能很是豐富的Java堆轉儲文件分析工具,並且簡單易用,只需點幾下下鼠標就能夠生成一個專業的分析報告。更重要的是,它和DDMS可以無縫集成,能夠幫助你發現Android程序中潛在的內存泄露問題,同時能夠協助你優化程序,更加高效的使用內存。數組

1、安裝 MATeclipse

MAT是Eclipse中的一個插件,因此安裝很是簡單。工具

首先,經過 Help -> Install Software... 啓動軟件安裝頁面:優化

選擇Add…,會彈出一個對話框,名字(Name)中填MAT(其實隨便填什麼),位置(Location)中填http://download.eclipse.org/mat/1.4/update-site/,而後點OK:插件

接下來選擇你想要安裝的 MAT 的功能。由於只須要在Eclipse中使用MAT,因此這裏只須要選擇Memory Analyzer for Eclipse IDE。特別提一下,Memory Analyzer (Chart) 這個功能是一個可選的安裝項,主要用來將數據生成相關的可視化報表,一般這樣更加直觀,更容易發現問題。線程

剩下的就一路點Next就行了。debug

2、幾個基本概念調試

要想看懂MAT生成的報告,還須要掌握幾個基本的概念。xml

Java內存回收

Java程序中,內存空間中垃圾回收的工做是由垃圾回收器(Garbage Collector,GC)完成的。它的核心思想是,對虛擬機中堆內存空間中的對象進行識別,若是對象正在被別的對象引用,那麼稱其爲存活對象;反之,若是對象再也不被任何別的對象所引用,則爲垃圾對象,能夠回收其佔據的空間,用於再分配。

對於Android程序來講,其Dalvik虛擬機自己就是一個類Java的虛擬機實現,也具備主動垃圾回收功能。

GC根對象(GC Root)

在垃圾回收機制中,有一組特殊的對象被稱爲根對象,它們是一組被虛擬機直接引用的對象。好比,正在運行的線程對象,系統調用棧裏面的對象,以及被系統類加載器所加載進來的對象。堆空間中的每一個對象都是由一個根對象爲起點被層層引用的,只有它們引用別的對象,沒有其它任何對象能夠引用它們。所以,若是一個對象還被某一個存活的根元素所直接或間接的引用,就會被認爲是存活對象,不能被回收,進行內存釋放。

Shallow Size和Retained Size

1)Shallow Size(表面大小)是指對象自己所佔用的內存空間大小,不包含被其引用到的對象所佔的內存空間。一個對象的Shallow Size取決於這個對象的實例變量的類型和個數。一個數組對象的Shallow Size是數組中保存的對象的Shallow Size乘以數組元素的個數。一個集合對象的Shallow Size是集合內全部對象的Shallow Size之和。

2)Retained Size(保留大小)是指對象自己的Shallow Size加上從其自己開始所能直接或間接訪問到的,且只能由其開始才能訪問到的全部對象的Shallow Size之和。這個大小就代表,若是將這個對象釋放以後,垃圾回收器所能回收的全部內存大小。

下面舉個例子說明,假設對象的引用關係以下:

第一張圖中計算對象obj1的Retained Size要包含obj一、obj2和obj4,由於obj3和obj5還能夠被根對象引用到,因此不能被包含進來。而在第二張圖中,obj3只能被obj1間接引用到,因此要包含進來。

支配樹(Dominator Tree)

支配樹的定義是,若是在對象引用關係圖中,從任意一個對象,若是想訪問對象Y的話,必須經過對象X,那麼對象X就處於對象Y的支配(Dominate)地位。

能夠看出來,支配樹的概念其實和前面的Retained Size是相關聯的。某個對象的Retained Size就是從這個對象開始,以及從它開始全部支配樹子節點的Shallow Size之和。

並且,全部根節點必定是支配樹中的頂層節點,反之不必定。

堆轉儲文件(Heap Dump)

所謂堆轉儲文件,是在一個特定時間點,對Java進程的內存快照文件。它包含了快照被觸發時,Java對象和類在堆中的使用狀況。因爲快照只是一瞬間的事情,因此只能包含在這個時間點各個對象之間的引用關係,而沒法知道一個對象在什麼時間點,由哪一個方法建立等信息。

MAT其實就是一個通用的Java堆轉儲文件的分析工具,而經過Android自帶的DDMS,能夠得到指定設備上指定進程的堆轉儲文件,將二者結合起來恰好能夠達到分析Android程序內存使用情況的目的。

3、如何分析

首先,將你想要分析的程序啓動起來,並確保其是可調試的(即在AndroidManifest.xml文件中申明android:debuggable=」true」)。

而後,用DDMS收集Android設備上,你想調試程序的堆轉儲文件。這步也很簡單,在DDMS中,選取你的程序,並點擊Dump HPROF file:

DDMS生成轉儲文件可能要花一點時間,請耐心等待一會。當轉儲文件生成好了以後,Eclipse會自動調用MAT進行分析,並生成報告:

在這份報告中,咱們能夠得到以下信息:

1)Histogram:列出了當前程序中每一個類被實例化出了多少個對象;

2)Dominator Tree:列出了當前程序中各個活動對象間的支配樹關係;

3)TopConsumer:列出了當前程序中Retained Size最大的前幾個對象等信息;

4)DuplicateClasses:列出了當前程序中是否有相同的類被不一樣的類加載器加載的狀況。

Histogram和Dominator Tree的區別是分析問題的角度不同,Histogram是站在類的角度上去分析,而Dominator Tree是站的對象實例的角度上去分析,Dominator Tree能夠更方便的看出各個對象間的引用關係。

有了這些武器以後,分析內存泄露問題就變得駕輕就熟了,通常的流程以下:

1)在Histogram和Dominator Tree視圖中,根據Retained Heap排序,找出排名靠前的幾個類和對象。這些都是後面要重點進行分析的對象。

2)每隔必定時間段生成一次MAT分析報告,對比這幾回報告,在在Histogram和Dominator Tree視圖中找出Retailed Heap不斷增大的幾個類和對象。持續佔用內存,還不釋放,一般意味着有潛在的內存泄露問題。

3)固然,若是一個對象的Retained Size很大,並不必定表明它必定是有問題的,還須要具體狀況具體分析。具體來講,咱們能夠分析一個對象到根對象的引用路徑來分析爲何該對象不能被順利回收。若是說一個對象已經不被任何程序邏輯所須要,可是還存在被根對象引用的狀況,基本上就能夠說這裏存在內存泄露的問題。經過Histogram或者Dominator Tree視圖找到疑似有問題的類或者對象以後,選擇Path to GC Roots或者Merge Shortest Paths to GC Roots。這裏有不少過濾選項,通常來說能夠選擇exclude all phantom/weak/soft etc.references。這樣就排除了虛引用、弱引用、以及軟引用,剩下的就是強引用。除了強引用外,其它任何類型的引用,在Java虛擬機須要的狀況下,均可以被GC釋放掉。若是一個對象始終沒法被釋放,確定是由於有強引用存在。

4)接下來就須要直接定位具體的代碼,看看如何釋放這些不應存在的對象。找到緣由,清理乾淨後,再對照以前的操做,看看對象是否任然持續增加。若是再也不,則說明這個潛在的內存泄露問題已經被修復了。

相關文章
相關標籤/搜索