java中如何使用mat分析java堆,相信很多沒有經(jīng)驗(yàn)的人對此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個(gè)問題。
成都創(chuàng)新互聯(lián)主營英吉沙網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營網(wǎng)站建設(shè)方案,重慶App定制開發(fā),英吉沙h5重慶小程序開發(fā)搭建,英吉沙網(wǎng)站營銷推廣歡迎英吉沙等地區(qū)企業(yè)咨詢
MAT
是 memory analyzer
的簡稱,它是一款功能強(qiáng)大的java堆內(nèi)存分析器,可以用來查找內(nèi)存泄露,以及查看內(nèi)存消耗的情況,可以在MAT官網(wǎng)進(jìn)行下載。
jmap
、jconsole
、jvisualvm
等工具可以導(dǎo)出java應(yīng)用程序的堆快照文件,MAT
也有該功能,如圖所示:
點(diǎn)擊“Accquire Heap Dump”菜單后,會(huì)彈出當(dāng)前java應(yīng)用程序列表,選擇要分析的應(yīng)用程序即可,如圖所示:
除了直接在MAT
中導(dǎo)出應(yīng)用程序的堆快照外,也可以通過“Open Heap Dump”來打開一個(gè)已有的堆快照文件。
如圖所示,顯示了正常打開堆快照文件后MAT
的界面:
在右側(cè)界面中,顯示了堆快照文件的大小、類、實(shí)例和ClassLoader的總數(shù)。
在餅圖中,顯示了當(dāng)前堆快照中最大的對象。
將鼠標(biāo)懸停在餅圖中,可以在左側(cè)的Inspector界面中,查看該對象的相應(yīng)信息。
在餅圖中,單擊某對象,可以對選中的對象進(jìn)行更多操作。
如圖所示,在工具欄上單擊柱狀圖,可以顯示系統(tǒng)中所有類的內(nèi)存使用情況:
MAT
也可以查看java線程,如圖所示:
當(dāng)然,這里查看java層面的應(yīng)用線程,虛擬機(jī)的系統(tǒng)線程是無法顯示的。通過線程的堆棧,還可以查看局部變量的信息。如下圖所示,帶有 <local>
標(biāo)記的為當(dāng)前幀棧的局部變量,這部分信息可能存在缺失。
除此之外,MAT
也可以查看詳細(xì)的線程堆棧信息:
由此打開thread_detail
標(biāo)簽頁,可以清晰地看到線程堆棧信息:
MAT
的另外一個(gè)常用功能,是在各個(gè)對象的引用列表中交叉查看。對于一個(gè)給定對象,通過MAT
可以找到引用當(dāng)前對象的對象,即入引用(Incomming References
),以及當(dāng)前對象引用的對象,即出引用(Outgoing References
),如圖所示:
淺堆(Shallow Heap)和深堆(Retained Heap)是兩個(gè)非常重要的概念,它們分別表示一個(gè)對象結(jié)構(gòu)所占用的內(nèi)存大小和一個(gè)對象被GC回收后,可以真實(shí)釋放的內(nèi)存大小。
淺堆是指一個(gè)對象所消耗的內(nèi)存。在32位系統(tǒng)中,一個(gè)對象引用會(huì)占據(jù)4字節(jié),一個(gè)int類型變量會(huì)占據(jù)4字節(jié),一個(gè)long類型變量會(huì)占8字節(jié),每個(gè)對象頭需要占8字節(jié)。根據(jù)堆快照格式不同,對象的大小可能會(huì)向8字節(jié)對齊。
根據(jù)堆快照格式不同,對象的大小可能會(huì)向8字節(jié)進(jìn)行對齊。以String對象為例,如下圖所示,顯示了String對象的幾個(gè)屬性。
String
value:char[]
offset:int
count:int
hash:int
3個(gè)int值共占12字節(jié),對象引用占用4字節(jié),對象頭8字節(jié),合計(jì)24字節(jié)。淺堆的大小只與對象的結(jié)構(gòu)有關(guān),與對象的實(shí)際內(nèi)容無關(guān)。也就是說,無論字符串的長度有多少,內(nèi)容是什么,淺堆的大小始終是24字節(jié)。
深堆(Retained Heap)的概念略微復(fù)雜。要理解深堆,首先需要了解保留集(Retained Set)。對象A的保留集指當(dāng)對象A被垃圾回收后,可以被釋放的所有的對象集合(包括對象A本身),即對象A的保留集可以被認(rèn)為是只能通過對象A被直接或間接訪問到的所有對象的集合。通俗地說,就是指僅被對象A所持有的對象的集合。深堆是指對象的保留集中所有的對象的淺堆大小之和。
注:淺堆指對象本身占用的內(nèi)存,不包括其內(nèi)部引用對象的大小。一個(gè)對象的深堆指只能通過該對象訪問到的(直接或間接)所有對象的淺堆之和,即對象被回收后,可以釋放的真實(shí)空間。
另外一個(gè)常用的概念是對象的實(shí)際大小。這里,對象的實(shí)際大小定義為一個(gè)對象所能觸及的所有對象的淺堆大小之和,也就是通常意義上我們說的對象大小。與深堆相比,似乎這個(gè)在日常開發(fā)中更為直觀和被人接受,但實(shí)際上,這個(gè)概念和垃圾回收無關(guān)。
如圖所示,顯示了一個(gè)簡單的對象引用關(guān)系圖,對象A引用了C和D,對象B引用了C和E。那么對象A的淺堆大小只是A本身,不含C和D,而A的實(shí)際大小為A、C、D三者之和。而A的深堆大小為A與D之和,由于對象C還可以通過對象B訪問到,因此不在對象A的深堆范圍內(nèi)。
MAT
提供了一個(gè)稱為支配樹(Dominator Tree
)的對象圖。支配樹體現(xiàn)了對象實(shí)例間的支配關(guān)系。在對象引用圖中,所有指向?qū)ο驜的路徑都經(jīng)過對象A,則認(rèn)為對象A支配對象B.如果對象A是離對象B最近的一個(gè)支配對象,則認(rèn)為對象A為對象B的直接支配者。支配樹是基于對象間的引用圖所建立的,它有以下基本性質(zhì):
對象A的子樹(所有被對象A支配的對象集合)表示對象A的保留集(Reatined Set),即深堆。
如果對象A支配對象B,那么對象A的直接支配者也支配對象B.
支配樹的邊與對象引用圖的邊不直接對應(yīng)。
如圖所示,左圖表示對象引用圖,右圖表示左圖所對應(yīng)的支配樹。對象A和B由根對象直接支配,由于在到對象C的路徑中,可以經(jīng)過A,也可以經(jīng)過B,因此對象C的直接支配者也是根對象。對象F與對象D相互引用,因?yàn)榈綄ο驠的所有路徑必然經(jīng)過對象D,因此,對象D是對象F的直接支配者。而到對象D的所有路徑中,必然經(jīng)過對象C,即使是從對象F到對象D的引用,從根節(jié)點(diǎn)出發(fā),也是經(jīng)過對象C的,所以,對象D的直接支配者為對象C。
在MAT中,單擊工具欄上的對象支配樹按鈕,可以打開對象支配樹視圖:
注:在對象支配樹中,某個(gè)對象的子樹表示在該對象被回收后也將被回收的對象的集合。
MAT
堆分析案例解析首先準(zhǔn)備案例代碼:
package jvm.chapter07; import java.util.List; import java.util.Vector; /** * {這里添加描述} * * @author chengyan * @date 2019-11-15 11:01 下午 */ public class Demo04 { private static List<WebPage> webPages = new Vector<>(); public static void createWebPages() { for(int i = 0; i < 100; i++) { WebPage webPage = new WebPage(); webPage.setUrl("http:www." + Integer.toString(i) + ".com"); webPage.setContent(Integer.toString(i)); webPages.add(webPage); } } public static void main(String[] args) { createWebPages(); Student student3 = new Student(3, "billy"); Student student5 = new Student(5, "alice"); Student student7 = new Student(7, "taotao"); for(int i = 0; i < webPages.size(); i++) { if(i % student3.getId() == 0) { student3.visit(webPages.get(i)); } if(i % student5.getId() == 0) { student5.visit(webPages.get(i)); } if(i % student7.getId() == 0) { student7.visit(webPages.get(i)); } } webPages.clear(); System.gc(); } } class Student { private int id; private String name; private List<WebPage> history = new Vector<>(); public void visit(WebPage webPage) { history.add(webPage); } public Student(int id, String name) { this.id = id; this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public List<WebPage> getHistory() { return history; } public void setHistory(List<WebPage> history) { this.history = history; } } class WebPage { private String url; private String content; public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } }
可以看到,在 Demo04.java
類中,首先創(chuàng)建了100個(gè)網(wǎng)址,為了閱讀方便,這里的網(wǎng)址均以數(shù)字作為域名,分別為0~99.之后,程序創(chuàng)建了3名學(xué)生:billy、alice和taotao.他們分別瀏覽了能被3、5、7整除的網(wǎng)址。在程序運(yùn)行后,3名學(xué)生的history中應(yīng)該保存他們各自訪問過的網(wǎng)址?,F(xiàn)在,希望在程序退出前,得到系統(tǒng)的堆信息并加以分析,查看每個(gè)學(xué)生實(shí)際訪問的網(wǎng)址。
使用如下參數(shù)運(yùn)行程序:
-XX:+HeapDumpBeforeFullGC -XX:HeapDumpPath=./stu.hprof
MAT
打開堆文件使用MAT
打開產(chǎn)生的stu.hprof文件,如圖所示:
右側(cè)界面中,顯示了堆快照文件的大小、類、實(shí)例和ClassLoader的總數(shù)。在右側(cè)的餅圖中,顯示了當(dāng)前堆快照中最大的對象。將鼠標(biāo)懸停在餅圖中,可以在左側(cè)的Inspector界面中,查看該對象的相應(yīng)信息。在餅圖中單擊某對象,可以對選中的對象進(jìn)行更多的操作。
在線程視圖中可以通過main線程
找到3名學(xué)生的引用,如圖所示,可以清晰看到三個(gè)對象的學(xué)生名。除了對象名稱,MAT
還給出了淺堆大小和深堆大小??梢钥吹?,所有的Student類的淺堆統(tǒng)一為24字節(jié),和他們持有的內(nèi)容無關(guān)。而深堆大小各不相同,這和每名學(xué)生訪問的網(wǎng)址有關(guān)。
當(dāng)然,這里查看Java層面的應(yīng)用線程,對于虛擬機(jī)的系統(tǒng)線程是無法顯示的。通過線程的堆棧,還可以查看局部變量的信息。帶有 <local>
標(biāo)記的,就為當(dāng)前幀棧的局部變量,這部分信息可能存在缺失。
為了獲得taotao同學(xué)訪問過的網(wǎng)址,可以在taotao的記錄中通過“出引用”(Outgoing References
)查找,可以找到由taotao可以觸及的對象,也就是他們訪問過的網(wǎng)址,如圖所示:
訪問過的網(wǎng)址如下:
可以看到堆中完整顯示了所有taobao同學(xué)的history中的網(wǎng)址。
如果現(xiàn)在希望查看哪些同學(xué)訪問了“http://www.0.com”,則可以在對應(yīng)的WebPage對象中通過“入引用”(Incoming References
)查找:
顯然,這個(gè)網(wǎng)址被3名學(xué)生都訪問過了。
如圖所示,顯示了Main Thread
的對象支配樹。被Student
對象直接支配的對象,均放在該對象下的history
中,即當(dāng)Student
對象被回收時(shí),也會(huì)一并回收該對象所支配的所有對象。
另外,還有一部分WebPage
的父節(jié)點(diǎn)是Thread
,這表明這部分的WebPage
同時(shí)被多個(gè)Student
對象持有,例如,84能同時(shí)被3和7整除,這表明該對象會(huì)同時(shí)被billy
與taobao
持有,當(dāng)billy
或taobao
其中之一并回收時(shí),該對象并不會(huì)回收,因此不會(huì)單獨(dú)顯示在billy
或taobao
的history
中。
在工具欄上單擊柱狀圖,可以顯示系統(tǒng)中所有類的內(nèi)存使用情況。圖為系統(tǒng)內(nèi)所有類的統(tǒng)計(jì)信息,包含類的實(shí)例數(shù)量和占用的空間。
為了方便查看,柱狀圖還提供了根據(jù)Class Loader和包對類進(jìn)行排序。如下圖是按照包排序的柱狀圖輸出。
看完上述內(nèi)容,你們掌握java中如何使用mat分析java堆的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!
網(wǎng)站名稱:java中如何使用mat分析java堆
文章位置:http://chinadenli.net/article10/gdoggo.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站制作、、Google、品牌網(wǎng)站建設(shè)、營銷型網(wǎng)站建設(shè)、標(biāo)簽優(yōu)化
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)