利用 MAT 玩转 JVM 内存分析(一) - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
nkduqi
V2EX    Java

利用 MAT 玩转 JVM 内存分析(一)

  •  
  •   nkduqi 2019-06-08 14:13:47 +08:00 3548 次点击
    这是一个创建于 2319 天前的主题,其中的信息可能已经有所发展或是发生改变。

    尽管 JVM 提供了自动内存管理的机制,试图降低程序员的开发门槛,确实也实现了这一目标,在日常开发中,我们一般都不需要关心对象的内存释放。JVM 大部分都是使用 trace 算法来判断一个对象是否该被回收,那么 JVM 只能回收那些从 gc roots 不可达的对象。

    如果我们在使用某些大的对象、集合对象或者一些三方包里的资源,忘记及时释放资源的话,还是会造成 JVM 的内存泄漏或内存浪费的问题。因此,如果想成为更高阶的 Java 开发工程师,我们需要了解常见的问题排查的办法和工,这个系列的文章,准备介绍一个用来做 JVM 堆内存分析的工具 MAT ( Memory Aanlysis Tool )。

    MAT 的官网在: https://www.eclipse.org/mat/,可以看下它的介绍 MAT 是一款高性能、具备丰富功能的 Java 堆内存分析工具,可以用来排查内存泄漏和内存浪费的问题。

    一、安装和装设置

    1.1 mac 安装

    MAT 支持两种安装方式,一种是"单机版“的,也就是说用户不必安装 Eclipse IDE 环境,MAT 作为一个独立的 Eclipse RCP 应用运行;另一种是”集成版“的,也就是说 MAT 也可以作为 Eclipse IDE 的一部分,和现有的开发平台集成。

    这里我们考虑独立安装,在观望的下载页面,选择 mac os 版本的安装文件下载即可。

    MAT 的独立下载地址

    安装遇到的坑

    1. 启动直接报错,针对这个问题,我找到了这个答案:https://stackoverflow.com/questions/47909239/how-to-run-eclipse-memory-analyzer-on-mac-os,这个帖子里给出了两个方案:
      • 系统默认的 workspace 是只读的,更换掉即可。怎么更换呢,在文件/Applications/mat.app/Contents/Eclipse/MemoryAnalyzer.ini中进行修改。image.png
    • 在 mat 的安装目录下,我的机器是/Applications/mat.app/Contents/MacOS,执行./MemoryAnalyzer命令,这种只能通过命令启动,不能通过图表启动。

    关于方案 1,这篇文章讲得更细致: https://www.jianshu.com/p/9bbbe3c4cc8b

    1. 启动后,UI 界面没反应,参考: https://www.eclipse.org/forums/index.php/t/1090889/,换个包即可。这个问题我遇到过很多次。image.png

    1.2 mat 的设置

    配置 mat 的堆内存大小

    我的电脑是 8C16G 的,那理论上分析 10G 的堆文件没问题,但是 MAT 默认的配置没有这么大,需要在/Applications/mat.app/Contents/Eclipse/MemoryAnalyzer.ini文件中进行修改。如下图所示,我将我的 MAT 自己的运行时堆内存配置成了 6G。image.png

    配置 MAT 的使用

    MAT 的配置页面可以从 Window >Preferences 找到,如下图所示。 image.png

    MAT 的一般配置有几个选项

    1. Keep unreachable objects:如果勾选这个,则在分析的时候会包含 dump 文件中的不可达对象;

    2. Hide the getting started wizard:隐藏分析完成后的首页,控制是否要展示一个对话框,用来展示内存泄漏分析、消耗最多内存的对象排序。

    3. Hide popup query help:隐藏弹出查询帮助,除非用户通过 F1 或 Help 按钮查询帮助。

    4. Hide Welcome screen on launch:隐藏启动时候的欢迎界面

    5. Bytes Display:设置分析结果中内存大小的展示单位

    可以看出,MAT 不仅支持 HPROF 文件的分析,还支持 DTFJ 文件的分析。一般 sun 公司系列的 JVM 生成的 dump 文件都是 HPROF 格式的,IBM 的 JVM 生成的 dump 文件时 DTFJ 格式的。

    二、基本概念

    Heap Dump

    Heap Dump 是 Java 进程在某个时刻的内存快照,不同 JVM 的实现的 Heap Dump 的文件格式可能不同,进而存储的数据也可能不同,但是一般来说。

    Heap Dump 中主要包含当生成快照时堆中的 java 对象和类的信息,主要分为如下几类:

    • 对象信息:类名、属性、基础类型和引用类型

    • 类信息:类加载器、类名称、超类、静态属性

    • gc roots:JVM 中的一个定义,进行垃圾收集时,要遍历可达对象的起点节点的集合

    • 线程栈和局部变量:快照生成时候的线程调用栈,和每个栈上的局部变量

    Heap Dump 中没有包含对象的分配信息,因此它不能用来分析这种问题:一个对象什么时候被创建、一个对象时被谁创建的。

    Shallow vs. Retained Heap

    Shallow heap是一个对象本身占用的堆内存大小。一个对象中,每个引用占用 8 或 64 位,Integer 占用 4 字节,Long 占用 8 字节等等。

    Retained set,对于某个对象 X 来说,它的 Retained set 指的是如果 X 被垃圾收集器回收了,那么这个集合中的对象都会被回收,同理,如果 X 没有被垃圾收集器回收,那么这个集合中的对象都不会被回收。

    Retained heap,对象 X 的 Retained heap 指的时候它的 Retained set 中的所有对象的 Shallow si 的和,换句话说,Retained heap 指的是对象 X 的保留内存大小,即由于它的存活导致多大的内存也没有被回收。

    leading set,对象 X 可能不止有一个,这些对象统一构成了 leading set。如果 leading set 中的对象都不可达,那么这个 leading set 对应的 retained set 中的对象就会被回收。一般有以下几种情况:

    1. 某个类的所有实例对象,这个类对象就是 leading object
    2. 某个类记载器加载的所有类,以及这些类的实例对象,这个类加载器对象就是 leading object
    3. 一组对象,要达到其他对象的必经路径上的对象,就是 leading object

    在下面这张图中,A 和 B 是 gc roots 中的节点(方法参数、局部变量,或者调用了 wait()、notify()或 synchronized()的对象)等等。可以看出,E 的存在,会导致 G 无法被回收,因此 E 的 Retained set 是 E 和 G ; C 的存在,会导致 E、D、F、G、H 都无法被回收,因此 C 的 Retined set 是 C、E、D、F、G、H ; A 和 B 的存在,会导致 C、E、D、F、G、H 都无法被回收,因此 A 和 B 的 Retained set 是 A、B、C、E、D、F、G、H。image.png

    Dominator Tree

    MAT 根据堆上的对象引用关系构建了支配树( Dominator Tree ),通过支配树可以很方便得识别出哪些对象占用了大量的内存,并可以看到它们之间的依赖关系。

    如果在对象图中,从 gc root 或者 x 上游的一个节点开始遍历,x 是 y 的必经节点,那么就可以说 x 支配了 y (dominate)。

    如果在对象图中,x 支配的所有对象中,y 的距离最近,那么就可以说 x 直接支配(immediate dominate) y。

    支配树是基于对象的引用关系图建立的,在支配树中每个节点都是它的子节点的直接支配节点。基于支配树可以很清楚得看到对象之间的依赖关系。

    现在看个例子,在下面这张图中

    1. x 节点的子树就是所有被 x 支配的节点集合,也正式 x 的 retained set ;
    2. 如果 x 是 y 的直接支配节点,那么 x 的支配节点也可以支配 y
    3. 支配树中的边跟对象引用图中的引用关系并不是一一对应的。

    image.png

    Garbage Collection Roots

    在 MAT 中,gc roots 的概念跟研究垃圾收集算法时候的概念稍微有点不同。gc roots 中的对象,是指那些可以从堆外访问到的对象的集合。如果一个对象符合下面这些场景中的一个,就可以被认为是 gc roots 中的节点:

    1. System Class:由 bootstrap classloader 加载的类,例如 rt.jar ,里面的类的包名都是java.util.*开头的。
    2. JNI Local:native 代码中的局部变量,例如用户编写的 JNI 代码或 JVM 内部代码。
    3. JNI Global:native 代码中的全局变量,例如用户编写的 JNI 代码或 JVM 内部代码。
    4. Thread Block:被当前活跃的线程锁引用的对象。
    5. Thread:正在存活的线程
    6. Busy Monitor:调用了 wait()、notify()或 synchronized 关键字修饰的代码例如synchronized(object)synchronized方法。
    7. Java Local:局部变量。例如函数的输入参数、正在运行的线程栈里创建的对象。
    8. Native Stack:native 代码的输入或输出参数,例如用户定义的 JNI 代码或 JVM 的内部代码。在文件 /网络 IO 方法或反射方法的参数。
    9. Finalizable:在 finalize 队列中等待它的 finalizer 对象运行的对象。
    10. Unfinalized:重载了 finalize 方法,但是还没有进入 finalize 队列中的对象。
    11. Unreachable:从任何 gc roots 节点都不可达的对象,在 MAT 中将这些对象视为 root 节点,如果不这么做,就不能对这些对象进行分析。
    12. Java Stack Frame:Java 栈帧,用于存放局部变量。只在 dump 文件被解析的时候会将 java stack frame 视为对象。
    13. Unknown:没有 root 类型的对象。有些 dump 文件(例如 IBM 的 Portable Heap Dump )没有 root 信息。

    三、获取 Dump 文件

    1. 通过 MAT 生成 dump 文件 通过这个路径找到生成 dump 文件的对话框 image.png 选择一个进程,点击 finish 即可 image.png

    2. 通过 jmap 命令生成 dump 文件

      • 命令格式:jmap -dump:live,format=b,file=heap.bin <pid>
      • 注意:如果要保留 heapdump 中的不可达对象,则需要把”:live “去掉,即使用命令” jmap -dump,format=b,file=heap.bin <pid>“</pid>
    3. 通过设置 JVM 参数自动生成 使用-XX:+HeapDumpOnOutOfMemoryError这个 JVM 参数,在 Java 进程运行过程中发生 OOM 的时候就会生成一个 heapdump 文件,并写入到指定目录,一般用-XX:HeapDumpPath=${HOME}/logs/test来设置。


    本号专注于后端技术、JVM 问题排查和优化、Java 面试题、个人成长和自我管理等主题,为读者提供一线开发者的工作和成长经验,期待你能在这里有所收获。

    javaadu

    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5436 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 29ms UTC 07:47 PVG 15:47 LAX 00:47 JFK 03:47
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86