JVM - MAT进行内存泄漏检测

MAT

dump文件格式为:java_pid15032.hprof

MAT 工具是基于Eclipse 平台开发的,本身是一个Java 程序,是一款很好的内存分析工具,所以如果你的堆快照比较大的话,则需要一台内存比较大的分析机器,并给MAT 本身加大初始内存,这个可以修改安装目录中的MemoryAnalyzer.ini 文件。
在这里插入图片描述
在这里插入图片描述

概要

在这里插入图片描述

柱状图

在这里插入图片描述

MAT 中的Incoming/Outgoing References

在柱状图中,我们看到,其实它显示的东西跟jmap –histo 非常相似的,也就是类、实例、空间大小。
但是MAT 有一个专业的概念,这个可以显示对象的引入和对象的引出。
在Eclipse MAT 中,当右键单击任何对象时,将看到下拉菜单。如果选择“ListObjects”菜单项,则会注意到两个选项:

  1. with incoming references 对象的引入
  2. with outgoing references 对象的引出

解析:
在这里插入图片描述
代码中对象和引用关系如下:
对象A 和对象B 持有对象C 的引用
对象C 持有对象D 和对象E 的引用
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

这个outgoing references 和incoming references 非常有用,因为我们做MAT 分析一般时对代码不了解,排查内存泄漏也好,排查问题也好,垃圾回收中有一个很重要的概念,可达性分析算法,那么根据这个引入和引出,我就可以知道这些对象的引用关系,在MAT 中我们就可以知道比如A,B,C,D,E,F 之间的引用关系图,便于做具体问题的分析。

MAT 中的浅堆与深堆

浅堆(shallow heap) 代表了对象本身的内存占用,包括对象自身的内存占用,以及“为了引用”其他对象所占用的内存。
深堆(Retained heap) 是一个统计结果,会循环计算引用的具体对象所占用的内存。但是深堆和“对象大小”有一点不同,深堆指的是一个对象被垃圾回收后,能够释放的内存大小,这些被释放的对象集合,叫做保留集(Retained Set)

需要说明一下:JAVA 对象大小=对象头+实例数据+对齐填充

非数组类型的对象的shallow heap
shallow_size=对象头+各成员变量大小之和+对齐填充
其中,各成员变量大小之和就是实例数据,如果存在继承的情况,需要包括父类成员变量

数组类型的对象的shallow size
shallow size=对象头+类型变量大小数组长度+对齐填充,如果是引用类型,则是四字节或者八字节(64 位系统),如果是boolean 类型,则是一个字节
注意:这里类型变量大小
数组长度就是实例数据,强调是变量不是对象本身
在这里插入图片描述
总结: 我们可以看到在进行内存分析时,浅堆和深堆是两个非常重要的概念,尤其是深堆,影响着回收这个对象能够带来的垃圾回收的效果,所以在内存分析中,我们往往会去找那些深堆比较的大的对象,尤其是那些浅堆比较小但深堆比较大的对象,这些对象极有可能是问题对象。

使用MAT 进行内存泄漏检测

运行以下代码, 我们开始跑程序:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这里一个名称叫做king-thread 的线程,持有了超过99% 的对象,数据被一个HashMap 所持有。
这个就是内存泄漏的点,因为我代码中对线程进行了标识,所以像阿里等公司的编码规范中为什么一定要给线程取名字,这个是有依据的,如果不取名字的话,这种问题的排查将非常困难。
在这里插入图片描述
在这里插入图片描述
所以,如果是对于特别明显的内存泄漏,在这里能够帮助我们迅速定位,但通常内存泄漏问题会比较隐蔽,我们需要做更加复杂的分析。

支配树视图

支配树列出了堆中最大的对象,第二层级的节点表示当被第一层级的节点所引用到的对象,当第一层级对象被回收时,这些对象也将被回收。这个工具可以帮助我们定位对象间的引用情况,以及垃圾回收时的引用依赖关系。
支配树视图对数据进行了归类,体现了对象之间的依赖关系。我们通常会根据“深堆”进行倒序排序,可以很容易的看到占用内存比较高的几个对象,点击前面的箭头,即可一层层展开支配关系(依次找深堆明显比浅堆大的对象)。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
从上图层层分解,我们也知道,原来是king-thread 的深堆和浅堆比例很多(深堆比浅堆多很多、一般经验都是找那些浅堆比较小,同时深堆比较大的对象)

1、一个浅堆非常小的king-thread 持有了一个非常大的深堆
2、这个关系来源于一个HashMap
3、这个map 中有对象A,同时A 中引用了B,B 中引用了C
4、最后找到C 中里面有一个ArrayList 引用了一个大数据的数组。

经过分析,内存的泄漏点就在此。一个线程长期持有了200 个这样的数组,有可能导致内存泄漏。

MAT 中内存对比

我们对于堆的快照,其实是一个“瞬时态”,有时候仅仅分析这个瞬时状态,并不一定能确定问题,这就需要对两个或者多个快照进行对比,来确定一个增长趋势。
我们导出两份dump 日志,分别是上个例子中循环次数分别是10 和100 的两份日志
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

对比:打开柱状图,要注意通过包来分组快速找到我们项目中对象的类
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
经过内存日志的对比,分析出来这个类的对象的增长,也可以辅助到问题的定位(快速增加的地方有可能存在内存泄漏)

线程视图

想要看具体的引用关系,可以通过线程视图。线程在运行中是可以作为GC Roots 的。我们可以通过线程视图展示了线程内对象的引用关系,以及方法调用关系,相对比jstack 获取的栈dump,我们能够更加清晰地看到内存中具体的数据。
我们找到了king-thread,依次展开找到holder 对象,可以看到内存的泄漏点
在这里插入图片描述
在这里插入图片描述
还有另外一段是陷入无限循环,这个是相互引用导致的(进行问题排查不用被这种情况给误导了,这样的情况一般不会有问题—可达性分析算法的解决了相互引用的问题)。在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值