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