内存分析之MAT
文章目录
一,MAT工具简介
MAT(全名:Memory Analyzer Tool),是一款快速便捷且功能强大丰富的 JVM 堆内存离线分析工具。其通过展现 JVM 异常时所记录的运行时堆转储快照(Heap dump)状态(正常运行时也可以做堆转储分析),帮助定位内存泄漏问题或优化大内存消耗逻辑。
注意:MAT可独立于eclipse安装
应用场景
- 内存溢出,JVM堆区或方法区放不下存活及待申请的对象。如:高峰期系统出现 OOM(Out of Memory)异常,需定位内存瓶颈点来指导优化。
- 内存泄漏,不会再使用的对象无法被垃圾回收器回收。如:系统运行一段时间后出现 Full GC,甚至周期性 OOM 后需人工重启解决。
- 内存占用高。如:系统频繁 GC ,需定位影响服务实时性、稳定性、吞吐能力的原因。
常用且重要的功能点为: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:列出内存中的对象,对象的个数以及大小。Histogram在类的角度上进行分析,注重量的分析。
- Dominator Tree:列出最大的对象以及其依赖存活的Object (大小是以Retained Heap为标准排序的),是在对象实例的角度上进行分析,注重引用关系分析。它按对象的 Retain Heap 排序,也支持按多个维度聚类统计。
- Top Consumers : 通过图形列出最大的object
- Duplicate Class:通过MAT自动分析泄漏的原因
其中分析内存泄漏最常用的就是Histogram和Dominator Tree
(4) Reports:表示报告相关,包括以下两个部分:
- Leak Suspects:用于查找内存泄漏问题,以及系统概览
- Top Components:针对那些占用堆内存超过整个堆内存1%大小的组件做一系列的分析
(5) Step By Step:为MAT使用教程,只有一个Components Report。这个功能是一组功能的集合,用于分析某一类性的类的实例的问题,例如分析java.util.*
开头的类的实例对象的一些使用情况,例如:重复字符串、空集合、集合的使用率、软引用的统计、finalizer的统计、Map集合的碰撞率等等。
功能菜单
从左到右依次是:概览,类直方图,支配树,OQL查询,线程视图,报告相关,详细功能,对象查找
(1) 概览:即为OverView
(2) 类直方图:堆直方图是从类的角度看哪些类及该类的实例对象占用着内存情况,默认是按照某个类的shallow heap大小从大到小排序。
在直方图页面,还有一个重要的特性——可以选择一些其他维度进行分类分析,例如superclass、class loader、package。
右键某个类的时候会出现菜单栏,第一个【List objects】中有两个名词:
- with incoming references:直接引用了当前对象的对象,每个对象的 incoming references 可能有 0 到多个。
- with outGoing references:对象引用的外部对象(注意不包含对象的基本类型属性。基本属性内容可在 inspector 查看)。
(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) 报告相关
- Heap Dump Overview:整个堆的概括情况,例如:堆内存大小、对象个数、类的个数、类加载器的个数、GC root的个数、堆内存文件的格式、文件的创建时间、位置等信息。
- Leak Suspects:上面已有解释,排查潜在的内存泄漏问题。
- Top Components:上面已有解释,针对那些占用堆内存超过整个堆内存1%大小的组件做一系列的分析,例如:Top Consumers、保留集合、潜在的内存浪费问题等其他问题。
(7) 对象查找:MAT支持根据对象的十六进制地址查找对象的outbound引用视图
三,相关概念
3.1 Heap Dump
Heap Dump 是 Java 进程堆内存在一个时间点的快照,支持 HPROF 及 DTFJ 格式,前者由 Oracle 系列 JVM 生成,后者是 IBM 系列 JVM 生成。其内容主要包含以下几类:
- 所有对象的实例信息:对象所属类名、基础类型和引用类型的属性等。
- 所有类信息:类加载器、类名、继承关系、静态属性等。
- GC Root:GC Root 代表通过可达性分析来判定 JVM 对象是否存活的起始集合。JVM 采用追踪式垃圾回收(Tracing GC)模式,从所有 GC Roots 出发通过引用关系可以关联的对象就是存活的(且不可回收),其余的不可达的对象(Unreachable object:如果无法从 GC Root 找到一条引用路径能到达某对象,则该对象为Unreachable object)可以回收。
- 线程栈及局部变量:快照生成时刻的所有线程的线程栈帧,以及每个线程栈的局部变量。
3.2 Shallow Heap
对象自身占用的内存大小,不包括它引用的对象。如果是数组类型的对象,它的大小是数组元素的类型和数组长度决定。如果是非数组类型的对象,它的大小由其成员变量的数量和类型决定。
3.3 Retained Heap
一个对象的Retained Set所包含对象所占内存的总大小。换句话说,Retained Heap就是当前对象被GC后,从Heap上总共能释放掉的内存。
Retained Heap的概念,它表示如果一个对象被释放掉,那会因为该对象的释放而减少引用进而被释放的所有的对象(包括被递归释放的)所占用的heap大小。于是,如果一个对象的某个成员new了一大块int数组,那这个int数组也可以计算到这个对象中。相对于shallow heap,Retained heap可以更精确的反映一个对象实际占用的大小(因为如果该对象释放,retained heap都可以被释放)。
3.4 Dominator Tree
如果所有指向对象 Y 的路径都经过对象 X,则 X 支配(dominate) Y
Dominator tree 是根据对象引用及支配关系生成的整体树状图,支配树清晰描述了对象间的依赖关系。
支配关系还有如下关系:
- Dominator tree 中任一节点的子树就是被该节点支配的节点集合,也就是其 Retain Set。
- 如果 X 直接支配 Y,则 X 的所有支配节点均支配 Y。
3.5 Retained Set
一个对象的 Retained Set,指的是该对象被 GC 回收后,所有能被回收的对象集合。
另外,当该对象无法被 GC 回收,则其 Retained set 也必然无法被 GC 回收。
3.6 GC Root
GC发现通过任何reference chain(引用链)无法访问某个对象的时候,该对象即被回收。
名词GC Roots正是分析这一过程的起点,例如JVM自己确保了对象的可到达性(那么JVM就是GC Roots),所以GC Roots就是这样在内存中保持对象可到达性的,一旦不可到达,即被回收。
通常GC Roots是一个在current thread(当前线程)的call stack(调用栈)上的对象(例如方法参数和局部变量),或者是线程自身或者是system class loader(系统类加载器)加载的类以及native code(本地代码)保留的活动对象。
所以GC Roots是分析对象为何还存活于内存中的利器。