• Eclipse-MAT的使用案例


    学习 尚硅谷 宋红康 JVM从入门到精通 的学习笔记

    执行main方法

    启动的时候执行,虚拟机参数添加: -XX:+HeapDumpBeforeFullGC -XX:HeapDumpPath=d:\student.hprof

    /**
     * 有一个学生浏览网页的记录程序,它将记录 每个学生访问过的网站地址。
     * 它由三个部分组成:Student、WebPage和StudentTrace三个类
     * 启动的时候执行,虚拟机参数添加:  -XX:+HeapDumpBeforeFullGC -XX:HeapDumpPath=d:\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();
            //触发gc垃圾回收
            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;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105

    导入hprof文件开始分析

    image.png

    选择第一个, 生成怀疑泄漏的报告

    查看线程概述

    打开hprof文件后点击下面图片的图标来查看线程概述
    image.png


    image.png
    能看到main方法方法里面3个Student对象,三个student的shallow heap(浅堆)大小都是24, 但是三个对象的retained heap (深堆)都是不一样的.
    第一个student对象如果被回收的话,就会回收3784个字节
    第二个student对象如果被回收的话,就会回收1872个字节
    第三个student对象如果被回收的话,就会回收1384个字节
    为什么从第一个student对象到第三个student对象 深堆会越来越少呢???, 因为示例代码往三个student对象里面设置值
    image.png
    因为不同的分支条件不一样,进入第一个分支的多进入第二个分支的稍微少点,进入第三个分支的更少,所以第一个student对象深堆就大, 第二个student对象就稍微少点,第三个student对象就更少了.

    深堆数量是怎么算出来的

    image.png
    以最下面的student对象为例子,history深堆大小是1288 ,打开history这个ArrayList之后,发现ArrayList内部的elementData数组有一堆元素,再打开elementData数组之后,就能看到elementData这个数组里面有一堆WebPage对象了

    elementData数组的浅堆是80个字节,而elementData数组中的所有WebPage对象的深堆之和是1208个字节,所以加在一起就是elementData数组的深堆之和,也就是1288个字节.
    如果elementData数组要是被回收的话,只能回收掉1288个字节,原因是有一些WebPage还被别的student所引用,

    15个WebPage对象,每个对应152字节, 15*152=2280字节,即2280位elementData的实际大小,但是为什么elementData的深堆是1288呢?

    因为能被7整除且能被3整除,以及能被5整除的数值有: 0 21 35 42 63 84 70 ,总共七个数, 这七个数儿对应的WebPage不只是被这个student独享,还被其它的student对象引用了,这些是不能计算深堆里面的, 7*152就是1064个字节 ,

    image.png

    2280-1064 =1216 , 是1216这个数字,但是1216和显示的1288还是不一样,为什么这样呢?还差72个字节,这72个字节是什么呢? 因为elementData这个Object的数组引用也要占据一些空间
    image.png

    虽然elementData存了15个元素,但是不代表elementData数组长度就是15. 数组默认长度是10,如果装不下了就扩容成1.5倍,就变成了15了 ,数组长度15就能被第三个student对象装得下.

    15个elementData的元素,每个元素占用4个字节, 15*4 = 60字节.
    60+8个对象头的字节数+数组自己长度计数(4个字节) = 72字节
    所以这就匹配上了:

    因为第三个student对象在触发gc的时候只能有8个对象被回收掉, 每个占用152个字节,8个就是1216.
    1216再加上elementData元素的字节+对象头的字节数+数组自己长度计数 就是 1288个字节


    查看某个对象是否被别的对象引用

    通过这个功能可以查看某个对象是否能被GC清理掉,是否存在内存泄露问题

    image.png

    结果发现这个对象被三个对象引用了.

    image.png


    那么我们查看另外一个对象还有没有被其它对象引用
    image.png

    下图发现只有他自己引用了这个对象,那么当他自己被回收了之后,这个对象也会被回收
    image.png

  • 相关阅读:
    北京地标-自动驾驶高精度地图特征定位数据技术规范
    Elasticsearch检索
    containerd客户端比较
    Java多线程篇(3)——线程池
    Hadoop实训有谁会做有尝
    PHP 变动:PHP 8 版本下字符串与数值的弱比较
    9月1日目标检测学习笔记——文本检测
    成为会带团队的技术人 业务理解:深入业务是做好架构的前提
    利用mybatis-plus的分页插件在xml文件中联表查询实现分页(MySQL数据库)
    钉钉企业微应用开发C#-HTTP回调接口
  • 原文地址:https://blog.csdn.net/qq_41489540/article/details/126334371