MAT(全名:Memory Analyzer Tool),是一款快速便捷且功能强大丰富的 JVM 堆内存离线分析工具。其通过展现 JVM 异常时所记录的运行时堆转储快照(Heap dump)状态(正常运行时也可以做堆转储分析),帮助定位内存泄漏问题或优化大内存消耗逻辑。
注意:MAT可独立于eclipse安装
应用场景
常用且重要的功能点为:Histogram,Dominator Tree,Leak Suspects。
分析内存时只关注这几点基本可解决大部分问题
inspector
透视图,用于展示一个对象的详细信息,从上往下依次为:内存地址、加载器名称、包名、对象名称、对象所属的类的父类、对象所属的类的加载器对象、该对象的堆内存大小和保留大小,gc root信息。
用MAT打开hprof文件后会看到的标签页OverView主要内容是一个饼状图,它主要用来显示内存的消耗,饼状图的彩色区域代表被分配的内存,灰色区域的则是空闲内存,点击每个彩色区域可以看到这块区域的详细信息。
Heap Dump History
用于列举最近分析过的hprof文件
OverView
(1) Detail:展示堆的大小、类、对象以及类加载器的个数
(2) Biggest Objects by Retained Size:饼图,直观地描述了 dump 中占用内存最大的对象的前几名分布情况
(3) Actions:表示常用的分析动作,包括以下四个方面的内容:
其中分析内存泄漏最常用的就是Histogram和Dominator Tree
(4) Reports:表示报告相关,包括以下两个部分:
(5) Step By Step:为MAT使用教程,只有一个Components Report。这个功能是一组功能的集合,用于分析某一类性的类的实例的问题,例如分析java.util.*
开头的类的实例对象的一些使用情况,例如:重复字符串、空集合、集合的使用率、软引用的统计、finalizer的统计、Map集合的碰撞率等等。
功能菜单
从左到右依次是:概览,类直方图,支配树,OQL查询,线程视图,报告相关,详细功能,对象查找
(1) 概览:即为OverView
(2) 类直方图:堆直方图是从类的角度看哪些类及该类的实例对象占用着内存情况,默认是按照某个类的shallow heap大小从大到小排序。
在直方图页面,还有一个重要的特性——可以选择一些其他维度进行分类分析,例如superclass、class loader、package。
右键某个类的时候会出现菜单栏,第一个【List objects】中有两个名词:
(3) 支配树:即为Dominator Tree
(4) OQL查询:MAT提供另一种类似SQL的对象查询语言——OQL,可以用类似SQL语句的方式查询heap dump中的对象。OQL和关系型数据库具备类似的数据模型:将某个类看作是一张表,将该类的实例对象看作是该表中的行,每个对象中的属性看作是构成行的列。
语法结构如下:
SELECT *
FROM [ INSTANCEOF ] <class name="name">
[ WHERE <filter-expression> ]
</filter-expression></class>
OQL编辑器分为两个区域:
(5) 线程视图:线程视图给出了在生成快照那个时刻,JVM中的Java线程对象列表。
在线程视图这个表中,可以看到以下几个信息:线程对象的名字、线程名、线程对象占用的堆内存大小、线程对象的保留堆内存大小、线程的上下文加载器、是否为守护线程。
选中某个线程对象展开,可以看到线程的调用栈和每个栈的局部变量,通过查看线程的调用栈和局部变量的内存大小,可以找到在哪个调用栈里分配了大量的内存。
(6) 报告相关
(7) 对象查找:MAT支持根据对象的十六进制地址查找对象的outbound引用视图
Heap Dump 是 Java 进程堆内存在一个时间点的快照,支持 HPROF 及 DTFJ 格式,前者由 Oracle 系列 JVM 生成,后者是 IBM 系列 JVM 生成。其内容主要包含以下几类:
对象自身占用的内存大小,不包括它引用的对象。如果是数组类型的对象,它的大小是数组元素的类型和数组长度决定。如果是非数组类型的对象,它的大小由其成员变量的数量和类型决定。
一个对象的Retained Set所包含对象所占内存的总大小。换句话说,Retained Heap就是当前对象被GC后,从Heap上总共能释放掉的内存。
Retained Heap的概念,它表示如果一个对象被释放掉,那会因为该对象的释放而减少引用进而被释放的所有的对象(包括被递归释放的)所占用的heap大小。于是,如果一个对象的某个成员new了一大块int数组,那这个int数组也可以计算到这个对象中。相对于shallow heap,Retained heap可以更精确的反映一个对象实际占用的大小(因为如果该对象释放,retained heap都可以被释放)。
如果所有指向对象 Y 的路径都经过对象 X,则 X 支配(dominate) Y
Dominator tree 是根据对象引用及支配关系生成的整体树状图,支配树清晰描述了对象间的依赖关系。
支配关系还有如下关系:
一个对象的 Retained Set,指的是该对象被 GC 回收后,所有能被回收的对象集合。
另外,当该对象无法被 GC 回收,则其 Retained set 也必然无法被 GC 回收。
GC发现通过任何reference chain(引用链)无法访问某个对象的时候,该对象即被回收。
名词GC Roots正是分析这一过程的起点,例如JVM自己确保了对象的可到达性(那么JVM就是GC Roots),所以GC Roots就是这样在内存中保持对象可到达性的,一旦不可到达,即被回收。
通常GC Roots是一个在current thread(当前线程)的call stack(调用栈)上的对象(例如方法参数和局部变量),或者是线程自身或者是system class loader(系统类加载器)加载的类以及native code(本地代码)保留的活动对象。
所以GC Roots是分析对象为何还存活于内存中的利器。