溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

java中如何使用mat分析java堆

發(fā)布時間:2021-09-27 09:54:40 來源:億速云 閱讀:228 作者:柒染 欄目:編程語言

java中如何使用mat分析java堆,相信很多沒有經(jīng)驗的人對此束手無策,為此本文總結了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個問題。

MATmemory analyzer 的簡稱,它是一款功能強大的java堆內存分析器,可以用來查找內存泄露,以及查看內存消耗的情況,可以在MAT官網(wǎng)進行下載。

1. 初識

1.1 導出堆應用快照

jmap、jconsole、jvisualvm 等工具可以導出java應用程序的堆快照文件,MAT 也有該功能,如圖所示:

java中如何使用mat分析java堆

點擊“Accquire Heap Dump”菜單后,會彈出當前java應用程序列表,選擇要分析的應用程序即可,如圖所示:

java中如何使用mat分析java堆

1.2 打開堆快照文件

除了直接在MAT中導出應用程序的堆快照外,也可以通過“Open Heap Dump”來打開一個已有的堆快照文件。

如圖所示,顯示了正常打開堆快照文件后MAT的界面:

java中如何使用mat分析java堆

  • 在右側界面中,顯示了堆快照文件的大小、類、實例和ClassLoader的總數(shù)。

  • 在餅圖中,顯示了當前堆快照中最大的對象。

  • 將鼠標懸停在餅圖中,可以在左側的Inspector界面中,查看該對象的相應信息。

  • 在餅圖中,單擊某對象,可以對選中的對象進行更多操作。

1.3 查看所有類的內存使用情況

如圖所示,在工具欄上單擊柱狀圖,可以顯示系統(tǒng)中所有類的內存使用情況:

java中如何使用mat分析java堆

1.4 查看java線程

MAT 也可以查看java線程,如圖所示:

java中如何使用mat分析java堆

當然,這里查看java層面的應用線程,虛擬機的系統(tǒng)線程是無法顯示的。通過線程的堆棧,還可以查看局部變量的信息。如下圖所示,帶有 <local> 標記的為當前幀棧的局部變量,這部分信息可能存在缺失。

java中如何使用mat分析java堆

除此之外,MAT也可以查看詳細的線程堆棧信息:

java中如何使用mat分析java堆

由此打開thread_detail標簽頁,可以清晰地看到線程堆棧信息:

java中如何使用mat分析java堆

1.4 查看對象的引用

MAT 的另外一個常用功能,是在各個對象的引用列表中交叉查看。對于一個給定對象,通過MAT可以找到引用當前對象的對象,即入引用(Incomming References,以及當前對象引用的對象,即出引用(Outgoing References,如圖所示:

java中如何使用mat分析java堆

2. 淺堆與深堆

淺堆(Shallow Heap)和深堆(Retained Heap)是兩個非常重要的概念,它們分別表示一個對象結構所占用的內存大小和一個對象被GC回收后,可以真實釋放的內存大小。

淺堆是指一個對象所消耗的內存。在32位系統(tǒng)中,一個對象引用會占據(jù)4字節(jié),一個int類型變量會占據(jù)4字節(jié),一個long類型變量會占8字節(jié),每個對象頭需要占8字節(jié)。根據(jù)堆快照格式不同,對象的大小可能會向8字節(jié)對齊。

根據(jù)堆快照格式不同,對象的大小可能會向8字節(jié)進行對齊。以String對象為例,如下圖所示,顯示了String對象的幾個屬性。

  • String

    • value:char[]

    • offset:int

    • count:int

    • hash:int

3個int值共占12字節(jié),對象引用占用4字節(jié),對象頭8字節(jié),合計24字節(jié)。淺堆的大小只與對象的結構有關,與對象的實際內容無關。也就是說,無論字符串的長度有多少,內容是什么,淺堆的大小始終是24字節(jié)。

深堆(Retained Heap)的概念略微復雜。要理解深堆,首先需要了解保留集(Retained Set)。對象A的保留集指當對象A被垃圾回收后,可以被釋放的所有的對象集合(包括對象A本身),即對象A的保留集可以被認為是只能通過對象A被直接或間接訪問到的所有對象的集合。通俗地說,就是指僅被對象A所持有的對象的集合。深堆是指對象的保留集中所有的對象的淺堆大小之和。

注:淺堆指對象本身占用的內存,不包括其內部引用對象的大小。一個對象的深堆指只能通過該對象訪問到的(直接或間接)所有對象的淺堆之和,即對象被回收后,可以釋放的真實空間。

另外一個常用的概念是對象的實際大小。這里,對象的實際大小定義為一個對象所能觸及的所有對象的淺堆大小之和,也就是通常意義上我們說的對象大小。與深堆相比,似乎這個在日常開發(fā)中更為直觀和被人接受,但實際上,這個概念和垃圾回收無關。

如圖所示,顯示了一個簡單的對象引用關系圖,對象A引用了C和D,對象B引用了C和E。那么對象A的淺堆大小只是A本身,不含C和D,而A的實際大小為A、C、D三者之和。而A的深堆大小為A與D之和,由于對象C還可以通過對象B訪問到,因此不在對象A的深堆范圍內。

java中如何使用mat分析java堆

3 支配樹

MAT 提供了一個稱為支配樹(Dominator Tree)的對象圖。支配樹體現(xiàn)了對象實例間的支配關系。在對象引用圖中,所有指向對象B的路徑都經(jīng)過對象A,則認為對象A支配對象B.如果對象A是離對象B最近的一個支配對象,則認為對象A為對象B的直接支配者。支配樹是基于對象間的引用圖所建立的,它有以下基本性質:

  • 對象A的子樹(所有被對象A支配的對象集合)表示對象A的保留集(Reatined Set),即深堆。

  • 如果對象A支配對象B,那么對象A的直接支配者也支配對象B.

  • 支配樹的邊與對象引用圖的邊不直接對應。

如圖所示,左圖表示對象引用圖,右圖表示左圖所對應的支配樹。對象A和B由根對象直接支配,由于在到對象C的路徑中,可以經(jīng)過A,也可以經(jīng)過B,因此對象C的直接支配者也是根對象。對象F與對象D相互引用,因為到對象F的所有路徑必然經(jīng)過對象D,因此,對象D是對象F的直接支配者。而到對象D的所有路徑中,必然經(jīng)過對象C,即使是從對象F到對象D的引用,從根節(jié)點出發(fā),也是經(jīng)過對象C的,所以,對象D的直接支配者為對象C。

java中如何使用mat分析java堆

在MAT中,單擊工具欄上的對象支配樹按鈕,可以打開對象支配樹視圖:

java中如何使用mat分析java堆

注:在對象支配樹中,某個對象的子樹表示在該對象被回收后也將被回收的對象的集合。

4. MAT 堆分析案例解析

首先準備案例代碼:

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個網(wǎng)址,為了閱讀方便,這里的網(wǎng)址均以數(shù)字作為域名,分別為0~99.之后,程序創(chuàng)建了3名學生:billy、alice和taotao.他們分別瀏覽了能被3、5、7整除的網(wǎng)址。在程序運行后,3名學生的history中應該保存他們各自訪問過的網(wǎng)址?,F(xiàn)在,希望在程序退出前,得到系統(tǒng)的堆信息并加以分析,查看每個學生實際訪問的網(wǎng)址。

使用如下參數(shù)運行程序:

-XX:+HeapDumpBeforeFullGC -XX:HeapDumpPath=./stu.hprof
4.1 使用MAT打開堆文件

使用MAT打開產(chǎn)生的stu.hprof文件,如圖所示:

java中如何使用mat分析java堆

右側界面中,顯示了堆快照文件的大小、類、實例和ClassLoader的總數(shù)。在右側的餅圖中,顯示了當前堆快照中最大的對象。將鼠標懸停在餅圖中,可以在左側的Inspector界面中,查看該對象的相應信息。在餅圖中單擊某對象,可以對選中的對象進行更多的操作。

4.2 查看線程

在線程視圖中可以通過main線程找到3名學生的引用,如圖所示,可以清晰看到三個對象的學生名。除了對象名稱,MAT還給出了淺堆大小和深堆大小??梢钥吹?,所有的Student類的淺堆統(tǒng)一為24字節(jié),和他們持有的內容無關。而深堆大小各不相同,這和每名學生訪問的網(wǎng)址有關。

java中如何使用mat分析java堆

當然,這里查看Java層面的應用線程,對于虛擬機的系統(tǒng)線程是無法顯示的。通過線程的堆棧,還可以查看局部變量的信息。帶有 <local> 標記的,就為當前幀棧的局部變量,這部分信息可能存在缺失。

4.3 查看對象的出引用

為了獲得taotao同學訪問過的網(wǎng)址,可以在taotao的記錄中通過“出引用”(Outgoing References)查找,可以找到由taotao可以觸及的對象,也就是他們訪問過的網(wǎng)址,如圖所示:

java中如何使用mat分析java堆

訪問過的網(wǎng)址如下:

java中如何使用mat分析java堆

可以看到堆中完整顯示了所有taobao同學的history中的網(wǎng)址。

4.4 查看入引用

如果現(xiàn)在希望查看哪些同學訪問了“http://www.0.com”,則可以在對應的WebPage對象中通過“入引用”(Incoming References)查找:

java中如何使用mat分析java堆

顯然,這個網(wǎng)址被3名學生都訪問過了。

java中如何使用mat分析java堆

4.5 查看支配樹

如圖所示,顯示了Main Thread的對象支配樹。被Student對象直接支配的對象,均放在該對象下的history中,即Student對象被回收時,也會一并回收該對象所支配的所有對象。

另外,還有一部分WebPage的父節(jié)點是Thread,這表明這部分的WebPage同時被多個Student對象持有,例如,84能同時被3和7整除,這表明該對象會同時被billytaobao持有,當billytaobao其中之一并回收時,該對象并不會回收,因此不會單獨顯示在billytaobaohistory中。

java中如何使用mat分析java堆

4.6 查看所有類的內存使用情況

在工具欄上單擊柱狀圖,可以顯示系統(tǒng)中所有類的內存使用情況。圖為系統(tǒng)內所有類的統(tǒng)計信息,包含類的實例數(shù)量和占用的空間。

java中如何使用mat分析java堆

為了方便查看,柱狀圖還提供了根據(jù)Class Loader和包對類進行排序。如下圖是按照包排序的柱狀圖輸出。

java中如何使用mat分析java堆

看完上述內容,你們掌握java中如何使用mat分析java堆的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業(yè)資訊頻道,感謝各位的閱讀!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內容。

AI