本文首发于公众号:javaadu

尽管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版本的安装文件下载即可。

安装遇到的坑

  1. 启动直接报错,针对这个问题,我找到了这个答案:https://stackoverflow.com/questions/47909239/how-to-run-eclipse-memory-analyzer-on-mac-os,这个帖子里给出了两个方案:

    • 系统默认的workspace是只读的,更换掉即可。怎么更换呢,在文件/Applications/mat.app/Contents/Eclipse/MemoryAnalyzer.ini中进行修改。
  • 在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/,换个包即可。这个问题我遇到过很多次

1.2 mat的设置

配置mat的堆内存大小

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

配置MAT的使用

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

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。

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. 支配树中的边跟对象引用图中的引用关系并不是一一对应的。

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文件的对话框



    选择一个进程,点击finish即可

  2. 通过jmap命令生成dump文件
    • 命令格式:jmap -dump:live,format=b,file=heap.bin <pid>
    • 注意:如果要保留heapdump中的不可达对象,则需要把”:live“去掉,即使用命令”jmap -dump,format=b,file=heap.bin
  3. 通过设置JVM参数自动生成

    使用-XX:+HeapDumpOnOutOfMemoryError这个JVM参数,在Java进程运行过程中发生OOM的时候就会生成一个heapdump文件,并写入到指定目录,一般用-XX:HeapDumpPath=${HOME}/logs/test来设置。


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

利用MAT玩转JVM内存分析(一)的更多相关文章

  1. 大数据学习--day13(字符串String--源码分析--JVM内存分析)

    字符串String--源码分析--JVM内存分析 String 类的对象 , 是不可变的字符串对象呢 这个不可变很重要,之后要讲的intern()也离不开它的不可变性. https://www.cnb ...

  2. 学习记录--JVM内存分析

    今天刷牛客网的题时,看到了两位大神关于JVM内存分析的讲解,就顺手记录学习一下. JVM内存模型图 方法区域存放了所加载的类的信息(名称.修饰符等).类中的静态变量.类中定义为final类型的常量.类 ...

  3. Day05_22_实例化对象的JVM内存分析

    创建对象的 JVM 内存分析 *new 运算符的作用是创建对象,在JVM堆内存中开辟新的内存空间 *方法区内存:在类加载的时候,class字节码文件被加载到该内存空间当中 *栈内存(局部变量):方法代 ...

  4. MAT(Memory Analyzer Tool)内存分析工具的使用

    开发.应用中老是会遇到OutOfMemory异常,而且常常是过一段时间内存才被吃光,这里可以利用java heap dump出jvm内存镜像,然后再对其进行分析来查找问题. 平常利用jmap -dum ...

  5. 说下Java堆空间结构,及常用的jvm内存分析命令和工具

    Java堆空间结构图:http://www.cnblogs.com/SaraMoring/p/5713732.html JVM内存状况查看方法和分析工具: http://blog.csdn.net/n ...

  6. JVM内存分析工具MAT使用

    1. 首先去官网下载MAT软件,路径如下: 点击打开链接 2. 将heap dump文件打开即可分析.

  7. MAT实战:JVM内存溢出的定位与分析

  8. JVM堆空间结构及常用的jvm内存分析命令和工具

    jdk8之前的运行时数据区域 程序计数器 是一块较小的内存空间,它可以看做是当前线程所执行的字节码的行号指示器.每个线程都有一个独立的程序计数器,这类内存区域为"线程私有",此内存 ...

  9. JVM内存分析

    1.java内存模型分析 java虚拟机运行时数据存储区域包括线程隔离和线程共享两类,整个PC的内存图如下所示: 下面对以上内存区域说明: 1.1 register和cache 当代计算机一般有多个c ...

随机推荐

  1. 50行ruby代码开发一个区块链

    区块链是什么?作为一个Ruby开发者,理解区块链的最好办法,就是亲自动手实现一个.只需要50行Ruby代码你就能彻底理解区块链的核心原理! 区块链 = 区块组成的链表? blockchain.ruby ...

  2. MySql在生产环境中是用mysqldump还是xtrabackup备份和恢复数据

    如题,究竟该使用mysqldump还是xtrabackup,要说用,两个都能备份,都支持热备,但是生产环境我们要考虑的是效率,就是不管备份还是恢复,都要快,要稳定. 之前我在维护mysql数据库的时候 ...

  3. activiti工作流框架简介

    常见的工作流框架:activiti, JBPM, OSWorkflow activiti框架基于23张基础的表数据, 基于Mybatis操作数据库. JBPM框架基于18张基础的表数据, 基于hibe ...

  4. windows10系统终极净化方法

    去年购入一台华硕FL8000U,性能很是不错,但是硬件只能兼容win10,不支持win7(linux倒是可以,但是始终用不顺手),win10里面杂七杂八的确实很多,本人重度强迫症+洁癖+极简主义,所以 ...

  5. Linux时间子系统之七:定时器的应用--msleep(),hrtimer_nanosleep()

    我们已经在前面几章介绍了低分辨率定时器和高精度定时器的实现原理,内核为了方便其它子系统,在时间子系统中提供了一些用于延时或调度的API,例如msleep,hrtimer_nanosleep等等,这些A ...

  6. mongodb的设计特征

    MongoDB 的设计目标是高性能.可扩展.易部署.易使用,存储数据非常方便.其主要功能特性如下.   (1)面向集合存储,容易存储对象类型的数据.在MongoDB 中数据被分组存储在集合中,集合类似 ...

  7. toString()方法详解

    在类型转换中,经常用到方法valueOf()和toString(),上一篇讲了valueOf()方法,这一篇来说说toString()方法.toSting()方法返回返回对象的字符串表现. [1]基本 ...

  8. CentOS 6.2+Nginx+Nagios,手机短信和qq邮箱提醒

    http://chenhao6.blog.51cto.com/6228054/1323192 标签:软件包 配置文件 nagios 服务端 监控 原创作品,允许转载,转载时请务必以超链接形式标明文章  ...

  9. Python将html转化为pdf

    前言 前面我们对博客园的文章进行了爬取,结果比较令人满意,可以一下子下载某个博主的所有文章了.但是,我们获取的只有文章中的文本内容,并且是没有排版的,看起来也比较费劲... 咋么办的?一个比较好的方法 ...

  10. celery学习笔记2

    1.定义: Celery是一个异步的任务队列(也叫做分布式任务队列) 2.工作结构 Celery分为3个部分 (1)worker部分负责任务的处理,即工作进程(我的理解工作进程就是你写的python代 ...