Eclipse Memory Analyzer 的使用
原文出處:郭霖,http://blog.csdn.net/sinyu890807/article/details/42238633?locationNum=4
Eclipse Memory Analyzer(MAT)是一款內(nèi)存分析工具,下載地址
這個工具分為Eclipse插件版和獨立版兩種,如果你是使用Eclipse開發(fā)的,那么可以使用插件版MAT,非常方便。如果你是使用Android Studio開發(fā)的,那么就只能使用獨立版的MAT了
下載好了之后下面我們開始學(xué)習(xí)如何去分析內(nèi)存泄露的原因,首先還是進入到DDMS界面,然后在左側(cè)面板選中我們要觀察的應(yīng)用程序進程,接著點擊Dump HPROF file按鈕,如下圖所示:
點擊這個按鈕之后需要等待一段時間,然后會生成一個HPROF文件,這個文件記錄著我們應(yīng)用程序內(nèi)部的所有數(shù)據(jù)。但是目前MAT還是無法打開這個文件的,我們還需要將這個HPROF文件從Dalvik格式轉(zhuǎn)換成J2SE格式,使用hprof-conv命令就可以完成轉(zhuǎn)換工作,如下所示:
hprof-conv dump.hprof converted-dump.hprofhprof-conv命令文件存放于/platform-tools目錄下面。另外如果你是使用的插件版的MAT,也可以直接在Eclipse中打開生成的HPROF文件,不用經(jīng)過格式轉(zhuǎn)換這一步。
好的,接下來我們就可以來嘗試使用MAT工具去分析內(nèi)存泄漏的原因了,這里需要提醒大家的是,MAT并不會準(zhǔn)確地告訴我們哪里發(fā)生了內(nèi)存泄漏,而是會提供一大堆的數(shù)據(jù)和線索,我們需要自己去分析這些數(shù)據(jù)來去判斷到底是不是真的發(fā)生了內(nèi)存泄漏。那么現(xiàn)在運行MAT工具,然后選擇打開轉(zhuǎn)換過后的converted-dump.hprof文件,如下圖所示:
MAT中提供了非常多的功能,這里我們只要學(xué)習(xí)幾個最常用的就可以了。上圖最中央的那個餅狀圖展示了最大的幾個對象所占內(nèi)存的比例,這張圖中提供的內(nèi)容并不多,我們可以忽略它。在這個餅狀圖的下方就有幾個非常有用的工具了,我們來學(xué)習(xí)一下。
Histogram可以列出內(nèi)存中每個對象的名字、數(shù)量以及大小。
Dominator Tree會將所有內(nèi)存中的對象按大小進行排序,并且我們可以分析對象之間的引用結(jié)構(gòu)。
一般最常用的就是以上兩個功能了,那么我們先從Dominator Tree開始學(xué)起。
現(xiàn)在點擊Dominator Tree,結(jié)果如下圖所示:
這張圖包含的信息非常多,我來帶著大家一起解析一下。首先Retained Heap表示這個對象以及它所持有的其它引用(包括直接和間接)所占的總內(nèi)存,因此從上圖中看,前兩行的Retained Heap是最大的,我們分析內(nèi)存泄漏時,內(nèi)存最大的對象也是最應(yīng)該去懷疑的。
另外大家應(yīng)該可以注意到,在每一行的最左邊都有一個文件型的圖標(biāo),這些圖標(biāo)有的左下角帶有一個紅色的點,有的則沒有。帶有紅點的對象就表示是可以被GC Roots訪問到的,根據(jù)上面的講解,可以被GC Root訪問到的對象都是無法被回收的。那么這就說明所有帶紅色的對象都是泄漏的對象嗎?當(dāng)然不是,因為有些對象系統(tǒng)需要一直使用,本來就不應(yīng)該被回收。我們可以注意到,上圖當(dāng)中所有帶紅點的對象最右邊都有寫一個System Class,說明這是一個由系統(tǒng)管理的對象,并不是由我們自己創(chuàng)建并導(dǎo)致內(nèi)存泄漏的對象。
那么上圖中就無法看出內(nèi)存泄漏的原因了嗎?確實,內(nèi)存泄漏本來就不是這么容易找出的,我們還需要進一步進行分析。上圖當(dāng)中,除了帶有System Class的行之外,最大的就是第二行的Bitmap對象了,雖然Bitmap對象現(xiàn)在不能被GC Roots訪問到,但不代表著Bitmap所持有的其它引用也不會被GC Roots訪問到?,F(xiàn)在我們可以對著第二行點擊右鍵 -> Path to GC Roots -> exclude weak references,為什么選擇exclude weak references呢?因為弱引用是不會阻止對象被垃圾回收器回收的,所以我們這里直接把它排除掉,結(jié)果如下圖所示:
可以看到,Bitmap對象經(jīng)過層層引用之后,到了MainActivity$LeakClass這個對象,然后在圖標(biāo)的左下角有個紅色的圖標(biāo),就說明在這里可以被GC Roots訪問到了,并且這是由我們自己創(chuàng)建的Thread,并不是System Class了,那么由于MainActivity$LeakClass能被GC Roots訪問到導(dǎo)致不能被回收,導(dǎo)致它所持有的其它引用也無法被回收了,包括MainActivity,也包括MainActivity中所包含的圖片。
通過這種方式,我們就成功地將內(nèi)存泄漏的原因找出來了。這是Dominator Tree中比較常用的一種分析方式,即搜索大內(nèi)存對象通向GC Roots的路徑,因為內(nèi)存占用越高的對象越值得懷疑。
接下來我們再來學(xué)習(xí)一下Histogram的用法,回到Overview界面,點擊Histogram,結(jié)果如下圖所示:
這里是把當(dāng)前應(yīng)用程序中所有的對象的名字、數(shù)量和大小全部都列出來了,需要注意的是,這里的對象都是只有Shallow Heap而沒有Retained Heap的,那么Shallow Heap又是什么意思呢?就是當(dāng)前對象自己所占內(nèi)存的大小,不包含引用關(guān)系的,比如說上圖當(dāng)中,byte[]對象的Shallow Heap最高,說明我們應(yīng)用程序中用了很多byte[]類型的數(shù)據(jù),比如說圖片。可以通過右鍵 -> List objects -> with incoming references來查看具體是誰在使用這些byte[]。
那么通過Histogram又怎么去分析內(nèi)存泄漏的原因呢?當(dāng)然其實也可以用和Dominator Tree中比較相似的方式,即分析大內(nèi)存的對象,比如上圖中byte[]對象內(nèi)存占用很高,我們通過分析byte[],最終也是能找到內(nèi)存泄漏所在的,但是這里我準(zhǔn)備使用另外一種更適合Histogram的方式。大家可以看到,Histogram中是可以顯示對象的數(shù)量的,那么比如說我們現(xiàn)在懷疑MainActivity中有可能存在內(nèi)存泄漏,就可以在第一行的正則表達式框中搜索“MainActivity”,如下所示:
可以看到,這里將包含“MainActivity”字樣的所有對象全部列出了出來,其中第一行就是MainActivity的實例。但是大家有沒有注意到,當(dāng)前內(nèi)存中是有11個MainActivity的實例的,這太不正常了,通過情況下一個Activity應(yīng)該只有一個實例才對。其實這些對象就是由于我們剛才不斷地橫豎屏切換所產(chǎn)生的,因為橫豎屏切換一次,Activity就會經(jīng)歷一個重新創(chuàng)建的過程,但是由于LeakClass的存在,之前的Activity又無法被系統(tǒng)回收,那么就出現(xiàn)這種一個Activity存在多個實例的情況了。
接下來對著MainActivity右鍵 -> List objects -> with incoming references查看具體MainActivity實例,如下圖所示:
如果想要查看內(nèi)存泄漏的具體原因,可以對著任意一個MainActivity的實例右鍵 -> Path to GC Roots -> exclude weak references,結(jié)果如下圖所示:
可以看到,我們再次找到了內(nèi)存泄漏的原因,是因為MainActivity$LeakClass對象所導(dǎo)致的。
好了,這大概就是MAT工具最常用的一些用法了,當(dāng)然這里還要提醒大家一句,工具是死的,人是活的,MAT也沒有辦法保證一定可以將內(nèi)存泄漏的原因找出來,還是需要我們對程序的代碼有足夠多的了解,知道有哪些對象是存活的,以及它們存活的原因,然后再結(jié)合MAT給出的數(shù)據(jù)來進行具體的分析,這樣才有可能把一些隱藏得很深的問題原因給找出來。
總結(jié)
以上是生活随笔為你收集整理的Eclipse Memory Analyzer 的使用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java基础:类加载器
- 下一篇: Notification详解