一、什么是heapdump
1、heapdump 介绍

heapdump文件是一个二进制文件,它保存了某一时刻JVM堆中对象使用情况。heapdump文件是指定时刻的Java堆栈的快照,是一种镜像文件。
Heap Dump中主要包含当生成快照时堆中的java对象和类的信息,主要分为如下几类:

  • 对象信息:类名、属性、基础类型和引用类型
  • 类信息:类加载器、类名称、超类、静态属性
  • gc roots:JVM中的一个定义,进行垃圾收集时,要遍历可达对象的起点节点的集合
  • 线程栈和局部变量:快照生成时候的线程调用栈,和每个栈上的局部变量
2、heapdump 用途

heapdump是诊断与JVM内存相关的问题的重要手段,例如:内存泄漏、垃圾回收问题和java.lang.OutOfMemoryError。同时也是优化内存消耗的重要手段。

3、JVM内存结构简述

说起heapdump,了解jvm 的内存结构,会更有助于对heapdump的使用。
JVM定义了若干个程序执行期间使用的数据区域。这个区域里的一些数据在JVM启动的时候创建,在JVM退出的时候销毁。而其他的数据依赖于每一个线程,在线程创建时创建,在线程退出时销毁。
jvm结构概览如下:(各块区域详细解释不在此说明,百度即可查到)
在这里插入图片描述
JVM内存模型中的这些区域,都是有大小限制的,当然也可以通过JVM提供的参数来设置这些区域所占内存的大小。
运行时各区块的描述如下
在这里插入图片描述
-Xms :初始堆大小(默认物理内存1/64);-Xmx :最大堆大小(默认物理内存1/4).。
-Xss:表示每个线程栈的大小。
-Xmn:表示新生代(年轻代)的大小
-XX:NewRatio:默认为2,表示新生代占年老代的1/2,占整个堆内存的1/3。
-XX:SurvivorRatio:默认为8,表示一个survivor区占用1/8的Eden内存,即1/10的新生代内存。
-XX:MaxMetaspaceSize: 设置元空间最大值, 默认是-1, 只受限于本地内存大小。
-XX:MetaspaceSize: 指定元空间触发Full Gc的初始阈值(元空间无固定初始大小), 以字节为单位。

在这里插入图片描述

内存为何会溢出

JVM根据generation(代)来进行GC,绝大多数的对象都在young generation被分配,也在young generation被收回,当young generation的空间被填满,GC会进行minor collection(次回收),速度非常快。其中,young generation中未被回收的对象被转移到tenured generation,当tenured generation被填满时,即触发major collection(FULL GC主回收),整个应用程序都会停止下来直到回收完成。

因此产生内存溢出错误原因一般出于以下原因:
1)JVM内存过小,或配置不合理
2)程序内存泄露导致的对象无法回收
3)产生的对象超过了超过了堆的大小

二、如何生成、导出heapdump
1、命令生成
jmap -dump:live,format=b,file=heapdump.hprof <pid>
#如下命令亦可
jcmd <pid> GC.heap_dump heapdump.hprof
2、配置Java 启动参数生成
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/tmp/heapdump.hprof
三、基于MAT进行heapdump 分析
1、MAT工具介绍

mat(Eclipse Memory Analyzer tool),是一个快速且功能丰富的Java堆分析器,可帮助您查找内存泄漏并减少内存消耗。使用Memory Analyzer分析具有数亿个对象的高效堆转储,快速计算对象的保留大小,查看谁阻止垃圾收集器收集对象,运行报告以自动提取泄漏嫌疑者。

2、名词术语解释

在使用mat 前,先了解一些术语,便于工具的使用。

  • Shallow heap:一个对象本身占用的堆内存大小,也就是对象头加成员变量(不是成员变量的值)的总和。一个对象中,每个引用占用8或64位,Integer占用4字节,Long占用8字节等等。
  • Retained Heap:
    如果一个对象被释放掉,那会因为该对象的释放而减少引用进而被释放的所有的对象(包括被递归释放的)所占用的heap大小,即对象被垃圾回收器回收后能被GC从内存中移除的所有对象之和。相对于shallow heap,Retained heap可以更精确的反映一个对象实际占用的大小(若该对象释放,retained heap都可以被释放)。
  • gc root: 在java语言中,都是通过可达性分析来判定对象是否存活的。此算法的基本思路是:通过一系列的称为“GC Roots”的对象作为起点,从这些节点向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连,则证明此对象是不可达的,因此能被GC 回收掉。因此可以得出,只有引用类型的变量才被认为是Roots,值类型的变量永远不被认为是Roots。GCROOT的目标对象是要以当前还在存活的对象集合,因此必须要选取确定存活的引用类型对象,GC管理的区域是java的堆,虚拟机栈、方法区和本地方法栈不被GC所管理,因此选用这些区域内引用的对象作为GC Roots,是不会被GC回收的。
3、MAT功能模块介绍

常用到的功能包括:Histogram直方图、Dominator Tree、Top consumers、Top consumers、Leak Suspects,如下进行逐一介绍。
打开dump后概览图如下:
mat 打开heapdump的概览图

为方便后续功能理解使用,先阐述几个通用功能
1.List object:其下有with outgoing references,with incoming references两个选项。
with outgoing references:查看当前对象持有的外部对象引用(在对象关系图中为从当前对象指向外的箭头)
with incoming references:查看当前对象被哪些外部对象所引用(在对象关系图中为指向当前对象的箭头)
2.Paths to GC Roots:从当前对象到GC roots的路径,这个路径解释了为什么当前对象还能存活,对分析内存泄露很有帮助,这个查询只能针对单个对象使用。其下有很多选项,在查询到GC root的路径时,是包含所有引用,还是排除一些类型的引用(如软引用、弱引用、虚引用),从GC角度说,一个对象无法被GC,一定是因为有强引用存在,其它引用类型在GC需要的情况下都是可以被GC掉的,所以可以使用 exclude all phantom/weak/soft etc. references 只查看GC路径上的强引用

  • Histogram:直方图,可以列出内存中的对象,对象的个数以及大小。
    在这里插入图片描述
    该视图以Class类的维度展示每个Class类的实例存在的个数、 占用的 [Shallow内存] 和 [Retained内存] 大小,可以分别排序显示。

从Histogram视图可以看出,哪个Class类的对象实例数量比较多,以及占用的内存比较大,Shallow Heap与Retained Heap的区别会在后面的概念介绍中说明。

不过,多数情况下,在Histogram视图看到实例对象数量比较多的类都是一些基础类型,如char[]、String、byte[],所以仅从这些是无法判断出具体导致内存泄露的类或者方法的,可以使用 List objects或 Merge Shortest Paths to GC roots 等功能继续钻取数据。如果Histogram视图展示的数量多的实例对象不是基础类型,是有嫌疑的某个类,如项目代码中的bean类型,那么就要重点关注了。

  • Dominator Tree:支配树,可以列出那个线程,以及线程下面的那些对象占用的空间。
    在这里插入图片描述
    该视图以实例对象的维度展示当前堆内存中Retained Heap占用最大的对象,以及依赖这些对象存活的对象的树状结构
    视图中展示了实例对象名、Shallow Heap大小、Retained Heap大小、以及当前对象的Retained Heap在整个堆中的占比
    Dominator Tree支配树可以很方便的找出占用Retained Heap内存最多的几个对象,并表示出某些objects的是因为哪些objects的原因而存活,在之后的 Dominator Tree概念 部分会对支配树做更详细的说明和举例

  • Top consumers:通过图形列出最大的object
    在这里插入图片描述
    可以通过按包名查看区分占用,根据包我们知道哪些公共用的到jar或自己的包占用

  • Thread Overview
    在这里插入图片描述
    在Thread Overview视图可以看到:线程对象/线程栈信息、线程名、Shallow Heap、Retained Heap、类加载器、是否Daemon线程等信息
    在分析内存Dump的MAT中还可以看到线程栈信息,这本身就是一个强大的功能,类似于jstack命令的效果
    而且还能结合内存Dump分析,看到线程栈帧中的本地变量,在左下方的对象属性区域还能看到本地变量的属性,真的很方便

  • Leak Suspects通过MA自动分析泄漏的原因
    在这里插入图片描述
    Leak Suspects 是MAT帮我们分析的可能有内存泄露嫌疑的地方,可以体现出哪些对象被保持在内存中,以及为什么它们没有被垃圾回收。MAT工具分析了heap dump后在界面上非常直观的展示了一个饼图,该图深色区域被怀疑有内存泄漏,
    接下来是一个简短的描述,告诉我们哪些线程占用了大量内存,并且明确指出system class loader加载的实例有内存聚集,并建议用关键字对应进行检查。在下面还有一个“Details”链接,可以查看明细信息。
    在这里插入图片描述

(1)Details的最开始是Description描述,和前一个页面对内存泄露嫌疑点的描述一致,下面有一些与怀疑的内存泄露点关联的查询结果展示,是分析报告中认为可能会存在问题,协助我们深入分析问题根源的。
(2)Shortest Paths To the Accumulation Point:当前对象的 Path to GC roots,即到GC roots的路径。作用是可以分析是由于和哪个GC root相连导致当前Retained Heap占用相当大的对象无法被回收。
(3)Accumulated Objects in Dominator Tree:以对象的维度展示了以当前对象为根的 Dominator Tree支配树,可以方便的看出受当前对象“支配”的对象中哪个占用Retained Heap比较大。
(4)Accumulated Objects by Class in Dominator Tree:展示了以当前对象为根的Dominator Tree支配树,并以Class类分组。
(5)Thread Detail:Detail明细的最后由于当前怀疑泄露点为main Thread线程对象,故展示了线程明细信息,调用栈信息,对分析内存溢出的发生位置很有帮忙

参考博客:
https://blog.csdn.net/weixin_35397235/article/details/114815878
https://blog.csdn.net/weixin_43706468/article/details/103849980

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐