Eclipse Memory Analyzer 的使用教程最實用


原文出處:郭霖,http://blog.csdn.net/sinyu890807/article/details/42238633?locationNum=4

Eclipse Memory Analyzer(MAT)是一款內存分析工具,下載地址

這個工具分為Eclipse插件版和獨立版兩種,如果你是使用Eclipse開發的,那么可以使用插件版MAT,非常方便。如果你是使用Android Studio開發的,那么就只能使用獨立版的MAT了

下載好了之后下面我們開始學習如何去分析內存泄露的原因,首先還是進入到DDMS界面,然后在左側面板選中我們要觀察的應用程序進程,接着點擊Dump HPROF file按鈕,如下圖所示:

這里寫圖片描述

點擊這個按鈕之后需要等待一段時間,然后會生成一個HPROF文件,這個文件記錄着我們應用程序內部的所有數據。但是目前MAT還是無法打開這個文件的,我們還需要將這個HPROF文件從Dalvik格式轉換成J2SE格式,使用hprof-conv命令就可以完成轉換工作,如下所示:

hprof-conv dump.hprof converted-dump.hprof 
  • 1

hprof-conv命令文件存放於/platform-tools目錄下面。另外如果你是使用的插件版的MAT,也可以直接在Eclipse中打開生成的HPROF文件,不用經過格式轉換這一步。

好的,接下來我們就可以來嘗試使用MAT工具去分析內存泄漏的原因了,這里需要提醒大家的是,MAT並不會准確地告訴我們哪里發生了內存泄漏,而是會提供一大堆的數據和線索,我們需要自己去分析這些數據來去判斷到底是不是真的發生了內存泄漏。那么現在運行MAT工具,然后選擇打開轉換過后的converted-dump.hprof文件,如下圖所示:

這里寫圖片描述

MAT中提供了非常多的功能,這里我們只要學習幾個最常用的就可以了。上圖最中央的那個餅狀圖展示了最大的幾個對象所占內存的比例,這張圖中提供的內容並不多,我們可以忽略它。在這個餅狀圖的下方就有幾個非常有用的工具了,我們來學習一下。

Histogram可以列出內存中每個對象的名字、數量以及大小。 
Dominator Tree會將所有內存中的對象按大小進行排序,並且我們可以分析對象之間的引用結構。

一般最常用的就是以上兩個功能了,那么我們先從Dominator Tree開始學起。

現在點擊Dominator Tree,結果如下圖所示:

這里寫圖片描述

這張圖包含的信息非常多,我來帶着大家一起解析一下。首先Retained Heap表示這個對象以及它所持有的其它引用(包括直接和間接)所占的總內存,因此從上圖中看,前兩行的Retained Heap是最大的,我們分析內存泄漏時,內存最大的對象也是最應該去懷疑的。

另外大家應該可以注意到,在每一行的最左邊都有一個文件型的圖標,這些圖標有的左下角帶有一個紅色的點,有的則沒有。帶有紅點的對象就表示是可以被GC Roots訪問到的,根據上面的講解,可以被GC Root訪問到的對象都是無法被回收的。那么這就說明所有帶紅色的對象都是泄漏的對象嗎?當然不是,因為有些對象系統需要一直使用,本來就不應該被回收。我們可以注意到,上圖當中所有帶紅點的對象最右邊都有寫一個System Class,說明這是一個由系統管理的對象,並不是由我們自己創建並導致內存泄漏的對象。

那么上圖中就無法看出內存泄漏的原因了嗎?確實,內存泄漏本來就不是這么容易找出的,我們還需要進一步進行分析。上圖當中,除了帶有System Class的行之外,最大的就是第二行的Bitmap對象了,雖然Bitmap對象現在不能被GC Roots訪問到,但不代表着Bitmap所持有的其它引用也不會被GC Roots訪問到。現在我們可以對着第二行點擊右鍵 -> Path to GC Roots -> exclude weak references,為什么選擇exclude weak references呢?因為弱引用是不會阻止對象被垃圾回收器回收的,所以我們這里直接把它排除掉,結果如下圖所示:

這里寫圖片描述

可以看到,Bitmap對象經過層層引用之后,到了MainActivity$LeakClass這個對象,然后在圖標的左下角有個紅色的圖標,就說明在這里可以被GC Roots訪問到了,並且這是由我們自己創建的Thread,並不是System Class了,那么由於MainActivity$LeakClass能被GC Roots訪問到導致不能被回收,導致它所持有的其它引用也無法被回收了,包括MainActivity,也包括MainActivity中所包含的圖片。

通過這種方式,我們就成功地將內存泄漏的原因找出來了。這是Dominator Tree中比較常用的一種分析方式,即搜索大內存對象通向GC Roots的路徑,因為內存占用越高的對象越值得懷疑。

接下來我們再來學習一下Histogram的用法,回到Overview界面,點擊Histogram,結果如下圖所示:

這里寫圖片描述

這里是把當前應用程序中所有的對象的名字、數量和大小全部都列出來了,需要注意的是,這里的對象都是只有Shallow Heap而沒有Retained Heap的,那么Shallow Heap又是什么意思呢?就是當前對象自己所占內存的大小,不包含引用關系的,比如說上圖當中,byte[]對象的Shallow Heap最高,說明我們應用程序中用了很多byte[]類型的數據,比如說圖片。可以通過右鍵 -> List objects -> with incoming references來查看具體是誰在使用這些byte[]。

那么通過Histogram又怎么去分析內存泄漏的原因呢?當然其實也可以用和Dominator Tree中比較相似的方式,即分析大內存的對象,比如上圖中byte[]對象內存占用很高,我們通過分析byte[],最終也是能找到內存泄漏所在的,但是這里我准備使用另外一種更適合Histogram的方式。大家可以看到,Histogram中是可以顯示對象的數量的,那么比如說我們現在懷疑MainActivity中有可能存在內存泄漏,就可以在第一行的正則表達式框中搜索“MainActivity”,如下所示:

這里寫圖片描述

可以看到,這里將包含“MainActivity”字樣的所有對象全部列出了出來,其中第一行就是MainActivity的實例。但是大家有沒有注意到,當前內存中是有11個MainActivity的實例的,這太不正常了,通過情況下一個Activity應該只有一個實例才對。其實這些對象就是由於我們剛才不斷地橫豎屏切換所產生的,因為橫豎屏切換一次,Activity就會經歷一個重新創建的過程,但是由於LeakClass的存在,之前的Activity又無法被系統回收,那么就出現這種一個Activity存在多個實例的情況了。

接下來對着MainActivity右鍵 -> List objects -> with incoming references查看具體MainActivity實例,如下圖所示:

這里寫圖片描述

如果想要查看內存泄漏的具體原因,可以對着任意一個MainActivity的實例右鍵 -> Path to GC Roots -> exclude weak references,結果如下圖所示:

這里寫圖片描述

可以看到,我們再次找到了內存泄漏的原因,是因為MainActivity$LeakClass對象所導致的。

好了,這大概就是MAT工具最常用的一些用法了,當然這里還要提醒大家一句,工具是死的,人是活的,MAT也沒有辦法保證一定可以將內存泄漏的原因找出來,還是需要我們對程序的代碼有足夠多的了解,知道有哪些對象是存活的,以及它們存活的原因,然后再結合MAT給出的數據來進行具體的分析,這樣才有可能把一些隱藏得很深的問題原因給找出來。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM