JVM监控及诊断工具之Eclipse MAT

1、简介

        MAT(Memory Analyzer Tool)工具是一款功能强大的Java堆内存分析器。可以用于查找内存泄露以及查看内存消耗情况。MAT是基于Eclipse开发的,不仅可以单独使用,还可以作为插件的形式嵌入在Eclipse中使用。是一款免费的性能分析工具,使用起来非常方便。

        MAT可以分析heap dump文件。在进行内存分析时,只要获得了反映当前设备内存映像的hprof文件,通过MAT打开就可以直观地看到当前的内存信息。一般来说,这些内存信息包含:

  • 所有的对象信息,包括对象实例、成员变量、存储于栈中的基本类型值和存储于堆中的其他对象的引用值。
  • 所有的类信息,包括classloader、类名称、父类、静态变量等
  • GCRoot到所有的这些对象的引用路径
  • 线程信息,包括线程的调用栈及此线程的线程局部变量。(TLS)

        MAT不是一个万能工具,它并不能处理所有类型的对存储文件。但是比较主流的厂家和格式。例如Sun,HP,SAP所采用的HPROF二进制堆存储文件,以及IBM的PHD堆存储文件等都能被很好的解析。

        最吸引人的还是能够快速为开发人员生成内存泄露报表,方便定位问题和分析问题。虽然MAT有如此强大的功能,但是内存分析也没有简单到一键完成的程度,很多内存问题还是需要我们从MAT展现给我们的信息当中通过经验和直觉来判断才能发现。

官方地址:https://www.eclipse.org/mat/downloads.php

JVM监控及诊断工具之Eclipse MAT

JVM监控及诊断工具之Eclipse MAT 点击上面的exe文件:

JVM监控及诊断工具之Eclipse MAT


2、获取dump文件方式

JVM监控及诊断工具之Eclipse MAT

JVM监控及诊断工具之Eclipse MAT

 2.1、下面我们先使用jmap生成dump文件

测试程序如下:

package com.kgf.kgfjavalearning2021.jvm.visualvm;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/***
 * -Xms600m -Xmx600m -XX:SurvivorRatio=8
 */
public class VisualVMTest {

    public static void main(String[] args) {

        List<Object> list = new ArrayList<>();
        while (true){
            try {
                Thread.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            list.add(new Picture(new Random().nextInt(100*50)));
        }
    }
}

class Picture{

    private byte[] pixels;

    public Picture(int length) {
        this.pixels = new byte[length];
    }
}

在idea中添加vm环境变量:

JVM监控及诊断工具之Eclipse MAT

 启动程序使用jmap命令:

JVM监控及诊断工具之Eclipse MAT

JVM监控及诊断工具之Eclipse MAT

 使用eclipse MAT打开:

JVM监控及诊断工具之Eclipse MAT

JVM监控及诊断工具之Eclipse MAT

 JVM监控及诊断工具之Eclipse MAT

2.2、分析堆dump文件

JVM监控及诊断工具之Eclipse MAT

histogram:

        展示了各个类的实例数目以及这些实例的Shallow heap或 者Retained heap的总和

JVM监控及诊断工具之Eclipse MAT

JVM监控及诊断工具之Eclipse MAT

thread overview:查看系统中的Java线程 查看局部变量的信息 

JVM监控及诊断工具之Eclipse MAT

JVM监控及诊断工具之Eclipse MAT

 获得对象互相引用的关系:with outgoing references

JVM监控及诊断工具之Eclipse MAT

JVM监控及诊断工具之Eclipse MAT

with incoming references :

JVM监控及诊断工具之Eclipse MAT

JVM监控及诊断工具之Eclipse MAT  

3、浅堆与深堆

3.1、浅堆(shallow heap)

JVM监控及诊断工具之Eclipse MAT

对象头代表根据类创建的对象的对象头,还有对象的大小不是可 能向8字节对齐,而是就向8字节对齐.

3.2、保留集(retained heap),也就是所谓的深堆

JVM监控及诊断工具之Eclipse MAT

 注意: 当前深堆大小 = 当前对象的浅堆大小 + 对象中所包含对象的深堆大小

3.3、补充:对象实际大小

JVM监控及诊断工具之Eclipse MAT 

4、练习

JVM监控及诊断工具之Eclipse MAT 

JVM监控及诊断工具之Eclipse MAT 

 JVM监控及诊断工具之Eclipse MAT

5、案例分析:StudentTrace

 代码如下:

package com.kgf.kgfjavalearning2021.jvm.mat;

import java.util.ArrayList;
import java.util.List;

/**
 * 有一个学生浏览网页的记录程序,它将记录 每个学生访问过的网站地址。
 * 它由三个部分组成:Student、WebPage和StudentTrace三个类
 *
 *  -XX:+HeapDumpBeforeFullGC -XX:HeapDumpPath=F:\mat_log\student.hprof
 * 
 */
public class StudentTrace {
    static List<WebPage> webpages = new ArrayList<WebPage>();

    public static void createWebPages() {
        for (int i = 0; i < 100; i++) {
            WebPage wp = new WebPage();
            wp.setUrl("http://www." + Integer.toString(i) + ".com");
            wp.setContent(Integer.toString(i));
            webpages.add(wp);
        }
    }

    public static void main(String[] args) {
        createWebPages();//创建了100个网页
        //创建3个学生对象
        Student st3 = new Student(3, "Tom");
        Student st5 = new Student(5, "Jerry");
        Student st7 = new Student(7, "Lily");

        for (int i = 0; i < webpages.size(); i++) {
            if (i % st3.getId() == 0)
                st3.visit(webpages.get(i));
            if (i % st5.getId() == 0)
                st5.visit(webpages.get(i));
            if (i % st7.getId() == 0)
                st7.visit(webpages.get(i));
        }
        webpages.clear();
        System.gc();
    }
}

class Student {
    private int id;
    private String name;
    private List<WebPage> history = new ArrayList<>();

    public Student(int id, String name) {
        super();
        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;
    }

    public void visit(WebPage wp) {
        if (wp != null) {
            history.add(wp);
        }
    }
}


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;
    }
}

在idea中配置环境变量:

JVM监控及诊断工具之Eclipse MAT

启动程序生成dump文件,使用MAT打开:

JVM监控及诊断工具之Eclipse MAT 结论:
        elementData数组的浅堆是80个字节,而elementData数组中的所有WebPage对象的深堆之和是1208个字节,所以加在一起就是elementData数组的深堆之和,也就是1288个字节
解释:
        我说“elementData数组的浅堆是80个字节”,其中15个对象一共是60个字节,对象头8个字节,数组对象本身4个字节,这些的和是72个字节,然后总和要是8的倍数,“elementData数组的浅堆是80个字节”我说“WebPage对象的深堆之和是1208个字节”,一共有15个对象,
其中0、21、42、63、84、35、70不仅仅是7的倍数,还是3或者5的倍数,所以这几个数值对应的i不能计算在深堆之内,这15个对象中大多数的深堆是152个字节,但是i是0和7的那两个深堆是144个字节,所以(13*152+144*2)-(6*152+144)=1208,所以这也印证了我上面的话,即“WebPage对象的深堆之和是1208个字节”因此“elementData数组的浅堆80个字节”加上“WebPage对象的深堆之和1208个字节”,正好是1288个字节,说明“elementData数组的浅堆1288个字节”

上一篇:eclipse创建springboot项目


下一篇:解决新版本eclipse中SpringMVC产生No mapping for GET的玄学方法