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

?点击上面的exe文件:

2、获取dump文件方式


?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环境变量:

?启动程序使用jmap命令:


?使用eclipse MAT打开:


?
2.2、分析堆dump文件

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


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


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


with incoming references?:

??
3、浅堆与深堆
3.1、浅堆(shallow heap)

对象头代表根据类创建的对象的对象头,还有对象的大小不是可 能向8字节对齐,而是就向8字节对齐.
3.2、保留集(retained heap),也就是所谓的深堆

?注意: 当前深堆大小 = 当前对象的浅堆大小 + 对象中所包含对象的深堆大小
3.3、补充:对象实际大小
?
4、练习
?
?
?
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中配置环境变量:

启动程序生成dump文件,使用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个字节”
|