MemoryAnalyze(MAT)的使用

 2023-01-14
原文作者:lxp_1101 原文地址:https://juejin.cn/post/6858981566405771277

1 生成Dump文件方式

1.1 VisualVM

先启动Java程序并且不能退出,打开VisualVM后就能看到了。如下图,鼠标双击

202301011709528971.png

202301011709533302.png

202301011709539953.png

1.2 通过jmap获取

    #获取Java进程
    jps -l
    #根据jps查看的进程号获取dump文件
    jmap -dump:format=b,file='文件路径' '进程号'

202301011709544894.png

1.3 mat

202301011709549355.png

2 浅堆(Shallow heap)和深堆(Retained heap)

2.1 浅堆

指的是对象自身所占据的内存,不包含其内部引用对象的大小

2.2 深堆

只能通过该对象访问到的(直接或者间接)所有对象的浅堆之和,即当对象不再被引用时,垃圾回收器所能回收的总内存,包括对象自身所占据的内存,以及仅能够通过该对象引用到的其他对象所占据的内存。

如下图所示,A的浅堆大小为A本身,由于C被B引用,所有A的深堆大小为A+D;同理可计算B的浅堆大小为B,深堆大小为B+E

202301011709554726.png

3 两种重要视图--直方图(histogram)和支配树(dominator tree)

如下图,是打开这两种视图的方式,可以看到两种视图的界面都会显示Shallow heap列和Retained heap列。

202301011709558917.png

202301011709563958.png

3.1 直方图(histogram)

MAT 的直方图和jmap的-histo子命令一样,都能够展示各个类的实例数目以及这些实例的 Shallow heap 总和。但是,MAT 的直方图还能够计算 Retained heap,并支持基于实例数目或 Retained heap 的排序方式(默认为 Shallow heap)。此外,MAT 还可以将直方图中的类按照超类、类加载器或者包名分组。

3.2 支配树(dominator tree)

在 A支配 B,且 A 不同于 B 的情况下(即 A严格支配 B),如果从 A节点到 B 节点的所有路径中不存在支配 B 的其他节点,那么 A直接支配(immediate dominate)B。这里的支配树指的便是由节点的直接支配节点所组成的树状结构。

支配树基本性质:

  1. 对象A的子树(所有被对象A支配的对象的集合)表示对象A的保留集(retained set),即深堆
  2. 如果对象A支配对象B,那么对象A的直接支配者也只支配对象B
  3. 支配树的边与对象引用图的边不直接对应

下图中,左图表示对象引用图,右图表示左图所对应的支配树。对象A和B由根对象直接支配,由于在到对象C的路径中,可以经过A,也可以经过B,因此对象 C的直接支配者也是根对象。对象F与对象D相互引用,因为到对象F的所有路径必然经过对象D,因此, 对象D是对象F的直接支配者。而到对象D的所有路径中,必然经过对象C,即使是从对象F 到对象D的引用,从根节点出发,也是经过对象C的,所以,对象D的直接支配者为对象C。

202301011709578349.png

4 查看GC Root

打开一个dump文件,按照下图的方式可以查看GC Root列表

2023010117095823810.png

2023010117095885511.png

2023010117095933812.png

下图是执行了main方法的线程,展开后可以看到main方法里有哪些属性作为GC Root

2023010117095989813.png

更为详细的解释,可以参考下面这条链接:[https://help.eclipse.org/2020-03/topic/org.eclipse.mat.ui.help/concepts/gcroots.html?cp=59_2_4](https://help.eclipse.org/2020-03/topic/org.eclipse.mat.ui.help/concepts/gcroots.html?cp=59_2_4)

5 查看入引用和出引用

如下图,鼠标右键任意一个对象,选择List objects都会显示 "with outgoing referances" 和 "with incoming referances"两个选项,分别表示出引用(当前对象引用的外部对象)和入引用(引用当前对象的对象)

2023010117100064014.png

5.1 示例代码

该程序用于模拟用户访问网站后,将网站的URL记录下来。先初始化100条记录,根据用户id进行取余,条件符合后存入用户历史访问记录集合中。

    public class WebPage {
        private String url;
        private String content;
        //get set方法省略
    }
    
    public class Student {
        private int id;
        private String name;
        private List history = new Vector();
    
        public Student(int id, String name) {
            this.id = id;
            this.name = name;
             //get set方法省略
        }
    }
    
    public class TraceStudent {
        static List webpages = new Vector<>();
        public static void createWebPages(){
            for(int i = 0; i < 100; i++){
                WebPage wp = new WebPage();
                wp.setUrl("http://www."+ Integer.valueOf(i) + ".com");
                wp.setContent(Integer.toString(i));
                webpages.add(wp);
            }
        }
    
        public static void main(String[] args) {
            createWebPages();
            Student st3 = new Student(3,"billy");
            Student st5 = new Student(5,"alice");
            Student st7 = new Student(7,"taotao");
            for(int i = 0; i < webpages.size(); i++){
                if(i % st3.getId() == 0){
                    st3.getHistory().add(webpages.get(i));
                }
                if(i % st5.getId() == 0){
                    st5.getHistory().add(webpages.get(i));
                }
                if(i % st7.getId() == 0){
                    st7.getHistory().add(webpages.get(i));
                }
            }
            System.gc();
    
        }
    }

5.2 运行参数

运行这个程序并使用下面的jvm参数,使得程序退出前能获得堆信息

    -XX:+HeapDumpBeforeFullGC -XX:HeapDumpPath=D:/tmp/tmp/stu.hprof

2023010117100125815.png

5.3 查看main方法对应的线程

参照第4小结打开GC Root页面,展开Thread列表,选择main线程,可以看到对应的实例对象

2023010117100222416.png

2023010117100312917.png

由于0对任意数取余都等于0,所有程序中三个Student对象都存储了这条URL,选择"with incoming referances"查看那些对象引用了下标为0的URL

2023010117100442418.png

这里显示了四条,除了3个Student对象外。还有一个用户初始化存储的集合webpages 也引用了,所以总共是4个。

2023010117100545619.png

对任意一个Student对象使用"with outgoing referances",可以查看当前对象引用了哪些外部对象

2023010117100637120.png

这个功能使用相对较少,因为在支配树(dominator tree)模式下,可以直接看到当前对象引用了哪些外部对象

2023010117100711821.png

6 使用MAT分析的一般步骤

6.1 选择Overview视图

2023010117100767122.png

6.2 鼠标左键点击饼状图占比较大的一部分,选择"with outgoing referances"查看

2023010117100879323.png

6.3 查看占用较大内存的对象

2023010117101009724.png