性能分析工具之-- Eclipse Memory Analyzer tool(MAT)(二)轉(zhuǎn)

前言

性能分析工具之-- Eclipse Memory Analyzer tool(MAT)(一)中介紹了內(nèi)存泄漏的前因后果。在本文中,將介紹MAT如何根據(jù)heapdump分析泄漏根源。由于測試范例可能過于簡單,很容易找出問題,但我期待借此舉一反三。

一開始不得不說說ClassLoader,本質(zhì)上,它的工作就是把磁盤上的類文件讀入內(nèi)存,然后調(diào)用java.lang.ClassLoader.defineClass方法告訴系統(tǒng)把內(nèi)存鏡像處理成合法的字節(jié)碼。Java提供了抽象類ClassLoader,所有用戶自定義類裝載器都實(shí)例化自ClassLoader的子類。systemclass loader在沒有指定裝載器的情況下默認(rèn)裝載用戶類,在Sun Java 1.5中既sun.misc.Launcher$AppClassLoader。更詳細(xì)的內(nèi)容請參看下面的資料。

準(zhǔn)備heap dump請看下面的Pilot類,沒啥特殊的。

/**?*?Pilot?class?*?@author?rosen?jiang?*/

package?org.rosenjiang.bo;

public?class?Pilot????{????????

String?name;????

int?age;????????

public?Pilot(String?a,?int?b){????????

????name?=?a;????????

????age?=?b; ??

?????}

}

然后再看OOMHeapTest類,它是如何撐破heapdump的。

/**?*?OOMHeapTest?class?*?

@author?rosen?jiang?*/

package?org.rosenjiang.test;

import?java.util.Date;

import?java.util.HashMap;

import?java.util.Map;

import?org.rosenjiang.bo.Pilot;

public?class?OOMHeapTest?{????

public?static?void?main(String[]?args){????????

oom();????}????????

private?static?void?oom()????{????????

Map?map?=?new?HashMap();????????

Object[]?array?=?new?Object[1000000];????????

for(int?i=0;?i<1000000;?i++)????{????????????

????String?d?=?new?Date().toString();????????????

????Pilot?p?=?new?Pilot(d,?i);????????????

????map.put(i+"rosen?jiang",?p);???????????

?????array[i]=p;???????

?????}????

????}

}

是的,上面構(gòu)造了很多的Pilot類實(shí)例,向數(shù)組和map中放。由于是StrongRef,GC自然不會回收這些對象,一直放在heap中直到溢出。當(dāng)然在運(yùn)行前,先要在Eclipse中配置VM參數(shù)-XX:+HeapDumpOnOutOfMemoryError。好了,一會兒功夫內(nèi)存溢出,控制臺打出如下信息。

java.lang.OutOfMemoryError:?Java?heap?spaceDumping?heap?to?java_pid3600.hprof?Heap?dump?file?created?[78233961?bytes?in?1.995?secs]Exception?in?thread?"main"?java.lang.OutOfMemoryError:?Java?heap?space

java_pid3600.hprof

既是heap dump,可以在OOMHeapTest類所在的工程根目錄下找到。


MAT安裝

話分兩頭說,有了heap dump還得安裝MAT。

MAT支持兩種安裝方式,一種是“獨(dú)立版本”,用戶不必安裝?EclipseIDE?環(huán)境,MAT?作為一個(gè)獨(dú)立的?EclipseRCP?應(yīng)用運(yùn)行;另一種是“插件版本”,也就是說MAT?可以作為?EclipseIDE?的一個(gè)插件,和Eclipse開發(fā)平臺集成。

獨(dú)立版本,下載地址:http://www.eclipse.org/mat/downloads.php

下載的zip包,解壓即可使用。下載頁圖示如下:


與eclipse IDE集成安裝過程,可參看以下文章:

http://www.ibm.com/developerworks/cn/opensource/os-cn-ecl-ma/index.html

安裝完成后,為了更有效率的使用?MAT,我們可以配置一些環(huán)境參數(shù)。因?yàn)橥ǔ6?,分析一個(gè)堆轉(zhuǎn)儲文件需要消耗很多的堆空間,為了保證分析的效率和性能,在有條件的情況下,我們會建議分配給?MAT?盡可能多的內(nèi)存資源。你可以采用如下兩種方式來分配內(nèi)存更多的內(nèi)存資源給?MAT。

一種是修改啟動參數(shù)?MemoryAnalyzer.exe-vmargs -Xmx4g

另一種是編輯文件?MemoryAnalyzer.ini,在里面添加類似信息?-vmargs– Xmx4g。

說明:

1. MemoryAnalyzer.ini中的參數(shù)一般默認(rèn)為-vmargs– Xmx1024m,這就夠用了。假如你機(jī)器的內(nèi)存不大,改大該參數(shù)的值,會導(dǎo)致MemoryAnalyzer啟動時(shí),報(bào)錯:Failed to create the Java Virtual Machine。

2.當(dāng)你導(dǎo)出的dump文件的大小大于你配置的1024m(說明1中,提到的配置:-vmargs– Xmx1024m),MAT輸出分析報(bào)告的時(shí)候,會報(bào)錯:An internal error occurred during: "Parsing heap dump from XXX”。適當(dāng)調(diào)大說明1中的參數(shù)即可。

至此,MAT?就已經(jīng)成功地安裝配置好了,在Eclipse的左上角有Open Heap Dump按鈕,按照剛才說的路徑找到j(luò)ava_pid3600.hprof文件并打開。

先檢查一下?MAT生成的一系列文件:(截圖來自另一個(gè)例子)


可以看到?MAT工具提供了一個(gè)很貼心的功能,將報(bào)告的內(nèi)容壓縮打包到一個(gè)?zip文件,并把它存放到原始堆轉(zhuǎn)儲文件的存放目錄下,這樣如果您需要和同事一起分析這個(gè)內(nèi)存問題的話,只需要把這個(gè)小小的?zip包發(fā)給他就可以了,不需要把整個(gè)堆文件發(fā)給他。并且整個(gè)報(bào)告是一個(gè)?HTML格式的文件,用瀏覽器就可以輕松打開。

使用MAT打開dump文件,等待一會后,會彈出向?qū)Ы缑?,保持默認(rèn)設(shè)置,直接點(diǎn)Finish即是分析內(nèi)存泄露問題。在點(diǎn)擊Finish后,會出現(xiàn)overview界面,您可以點(diǎn)擊工具欄上的?Leak Suspects?菜單項(xiàng)來生成內(nèi)存泄露分析報(bào)告,也可以直接點(diǎn)擊餅圖下方的?Reports->Leak Suspects鏈接來生成報(bào)告。如圖:


MAT工具分析了heap dump后在界面上非常直觀的展示了一個(gè)餅圖,該圖深色區(qū)域被懷疑有內(nèi)存泄漏,可以發(fā)現(xiàn)整個(gè)heap才64M內(nèi)存,深色區(qū)域就占了99.5%。接下來是一個(gè)簡短的描述,告訴我們main線程占用了大量內(nèi)存,并且明確指出system class loader加載的"java.lang.Thread"實(shí)例有內(nèi)存聚集,并建議用關(guān)鍵字"java.lang.Thread"進(jìn)行檢查。所以,MAT通過簡單的兩句話就說明了問題所在,就算使用者沒什么處理內(nèi)存問題的經(jīng)驗(yàn)。在下面還有一個(gè)"Details"鏈接,在點(diǎn)開之前不妨考慮一個(gè)問題:為何對象實(shí)例會聚集在內(nèi)存中,為何存活(而未被GC)?是的——Strong Ref,那么再走近一些吧。如圖:


點(diǎn)擊了"Details"鏈接之后,除了在上一頁看到的描述外,還有Shortest Paths To the Accumulation Point和Accumulated Objects部分,這里說明了從GC root到聚集點(diǎn)的最短路徑,以及完整的reference chain。觀察Accumulated Objects部分,java.util.HashMap和java.lang.Object[1000000]實(shí)例的retained heap(size)最大,在上一篇文章中我們知道retained heap代表從該類實(shí)例沿著reference chain往下所能收集到的其他類實(shí)例的shallow heap(size)總和,所以明顯類實(shí)例都聚集在HashMap和Object數(shù)組中了。這里我們發(fā)現(xiàn)一個(gè)有趣的現(xiàn)象,既Object數(shù)組的shallow heap和retained heap竟然一樣,通過Shallow and retained sizes一文可知,數(shù)組的shallow heap和一般對象(非數(shù)組)不同,依賴于數(shù)組的長度和里面的元素的類型,對數(shù)組求shallow heap,也就是求數(shù)組集合內(nèi)所有對象的shallow heap之和。好,再來看org.rosenjiang.bo.Pilot對象實(shí)例的shallow heap為何是16,因?yàn)閷ο箢^是8字節(jié),成員變量int是4字節(jié)、String引用是4字節(jié),故總共16字節(jié)。

在Accumulated Objects視圖中,retained heap占用最多的是hashMap和object數(shù)組,為啥它們會占用這么大的heap呢?這個(gè)時(shí)候需要分析hashMap和object數(shù)組中存放了一些什么對象?接著往下看,來到了Accumulated Objects by Class區(qū)域,顧名思義,這里能找到被聚集的對象實(shí)例的類名。org.rosenjiang.bo.Pilot類上頭條了,被實(shí)例化了290,325次,再返回去看程序,我承認(rèn)是故意這么干的。還有很多有用的報(bào)告可用來協(xié)助分析問題,只是本文中的例子太簡單,也用不上。


為了更多的了解MAT的功能,再舉一些例子(不提供對應(yīng)的代碼):

例子二:

通過MAT發(fā)現(xiàn)heap dump問題所在,就需要尋找導(dǎo)致內(nèi)存泄漏的代碼點(diǎn)。這時(shí)往往需要打開對象依賴關(guān)系樹形視圖,點(diǎn)擊如圖按鈕即可。


這時(shí)會看到如下視圖:


這個(gè)視圖的右邊大區(qū)域可以看到對象的依賴關(guān)系,選中某個(gè)對象以后可以在左邊小窗口查看對象的一些屬性。如果屬性的值是一些內(nèi)存地址你還可以點(diǎn)擊工具欄的搜索按鈕來搜索具體的對象信息。在進(jìn)行具體分析的時(shí)候MAT只是起了幫助你進(jìn)行分析的工具的功能,OOM問題分析沒有固定方法和準(zhǔn)則。只能發(fā)揮你敏銳的洞察力,結(jié)合源代碼,對內(nèi)存中的對象進(jìn)行分析從而找到代碼中的BUG.?

例子三:

如何查看某一個(gè)對象占用的內(nèi)存空間1.按以下方式打開新窗口即可?,如圖:


2.輸入類名(輸入類的全名)?,如圖:


參考資料:

http://www.blogjava.net/rosen/archive/2010/06/13/323522.html

http://seanhe.iteye.com/blog/898277

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容