JVM系列之:MAT工具使用教程

简介: JVM系列之:MAT工具使用教程

1.jpg

本文为《深入学习 JVM 系列》第二十三篇文章


Eclipse Memory Analyzer (MAT)是一个快速且功能丰富的Java堆分析器,可帮助您发现内存泄漏并减少内存消耗。


安装并启动


直接参考 Mac下MAT的安装

需要注意的是注意 JDK 版本和 MAT 版本的映射,最新的 MAT 版本为 1.12.0,需要在 JDK11 以上运行。


如果 JDK 版本为 11,且 MAT 版本是最新的,还需要修改 /Applications/mat.app/Contents/Eclipse/MemoryAnalyzer.ini,在文件中指定 JDK 版本,增加配置如下:


-vm D:/JAVA_HOME/bin/javaw.exe
复制代码


亲自尝试下载 1.8.1 和 1.9.2 版本的 MAT,但是安装并配置好,虽然可以启动,却不能正常使用(点击界面按钮没有反应),最终下载了 1.12.0 版本的 MAT。


另外考虑到自己平时接触到的项目大多都是基于 JDK8,所以我又尝试下载了 1.7.0 版本的 MAT。


注意要修改 /Applications/mat.app/Contents/Info.plist 文件内容:


<!-- to use a specific Java version (instead of the platform's default) uncomment one of the following options,
          or add a VM found via $/usr/libexec/java_home -V
        <string>-vm</string><string>/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Commands/java</string>
        <string>-vm</string><string>/Library/Java/JavaVirtualMachines/1.8.0.jdk/Contents/Home/bin/java</string>
        <string>-vm</string><string>/Library/Java/JavaVirtualMachines/jdk1.8.0_301.jdk/Contents/Home/bin/java</string>
        <string>-vm</string><string>/Library/Java/JavaVirtualMachines/jdk-9.0.4.jdk/Contents/Home/bin/java</string>
        <string>-vm</string><string>/Library/Java/JavaVirtualMachines/jdk-11.0.13.jdk/Contents/Home/bin/java</string>
      -->
      <string>-vm</string><string>/Library/Java/JavaVirtualMachines/jdk1.8.0_301.jdk/Contents/Home/bin/java</string>
      <string>-data</string><string>/Applications/mat.app/Contents/MacOS/workspace</string>
复制代码


直接启动 mat.app,会提示如下错误:


无法打开“mat.app”,因为无法验证开发者。
复制代码


可以修改 Mac 系统偏好设置——》安全与隐私——》点击左下角锁,输入密码——》通用——》点击允许打开。


1.7.0 版本的 MAT 启动后,UI 界面没反应,参考:www.eclipse.org/forums/inde…


mv /Users/xx/chrome下载/java相关/swt-4.7.1a-cocoa-macosx-x86_64/swt.jar /Applications/mat.app/Contents/Eclipse/plugins/org.eclipse.swt.cocoa.macosx.x86_64_3.104.2.v20160212-1350.jar
复制代码


获取dump


本地准备一段代码,这个代码会导致 JVM 堆内存溢出,方便我们演示 MAT 的效果。


@Data
@AllArgsConstructor
@NoArgsConstructor
public class Goods {
  private String name;
  private Double price;
  private String[] types;
}
//-Xms60m -Xmx60m  -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/Applications/mat.app/Contents/MacOS/workspace
public class OutOfMemoryTest {
  public static void main(String[] args) throws InterruptedException {
    Map<String, Goods> map = new HashMap<>();
    int counter = 1;
    while (true) {
      Thread.sleep(10);
      Goods goods = new Goods();
      String[] types = new String[counter];
      for (int i = 0; i < types.length; i++) {
        types[i] = "type" + i;
      }
      goods.setName("hresh" + counter);
      goods.setPrice(Double.valueOf(counter));
      goods.setTypes(types);
      map.put(goods.getName(), goods);
      if (counter % 100 == 0) {
        System.out.println("put" + counter);
      }
      counter++;
    }
  }
}
复制代码


上述代码是在一个无限循环体中不断创建 Goods 对象,每次循环 new 出来的对象的 types 属性还在不断的扩大。


执行上述代码时,指定好堆内存大小,过段时间就会报错,并生成 dump 文件。


启动代码,运行一段时间后报错如下:


java.lang.OutOfMemoryError: GC overhead limit exceeded
Dumping heap to /Applications/mat.app/Contents/MacOS/workspace/java_pid17847.hprof ...
Heap dump file created [74544120 bytes in 0.279 secs]
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
  at com.msdn.java.commandLine.mat.OutOfMemoryTest.main(OutOfMemoryTest.java:22)
复制代码


相较于通过命令来导出 dump 文件,这种方式属于是事后处理,需要等待当前 JVM 出现问题后才能生成 dump 文件,实时性不高,而且一般也不会添加 HeapDumpOnOutOfMemoryError 参数。


关于 HeapDumpOnOutOfMemoryError 参数讲解:


-XX:+HeapDumpOnOutOfMemoryError 参数表示当JVM发生OOM时,自动生成DUMP文件。
-XX:HeapDumpPath=${目录}参数表示生成DUMP文件的路径,也可以指定文件名称,例如:-XX:HeapDumpPath=${目录}/java_heapdump.hprof。如果不指定文件名,默认会在项目根目录下生成一个文件,文件名格式为:java_<pid>_<date>_<time>_heapDump.hprof。
复制代码


除此之外,还有几个命令:


-XX:+HeapDumpBeforeFullGC   当 JVM 执行 FullGC前
-XX:+HeapDumpAfterFullGC    当 JVM 执行 FullGC后
复制代码


除了通过配置 JVM 参数来生成 dump 文件,还可以通过 jmap 工具来生成任意进程的 dump 文件。


# 先找到PID
ps -ef | grep java
# jmap 转存快照
jmap -dump:format=b,file=/opt/dump/test.dump {PID}
复制代码


内存分析


使用 MAT 工具打开刚才生成的 dump 文件。需要注意的是,实际生产中获得的 dump 文件可能很大,MAT 载入会失败。可以尝试修改


/Applications/mat.app/Contents/Eclipse/MemoryAnalyzer.ini,如下所示:


-startup
../Eclipse/plugins/org.eclipse.equinox.launcher_1.3.100.v20150511-1540.jar
--launcher.library
../Eclipse/plugins/org.eclipse.equinox.launcher.cocoa.macosx.x86_64_1.1.300.v20150602-1417
-vmargs
-Xmx1024m
-Dorg.eclipse.swt.internal.carbon.smallFonts
-XstartOnFirstThread
复制代码


增加一下-Xmx 的值,实在不行,我们就换用 VisualVM 来分析 dump 文件。


当加载完堆快照之后,MAT 的主界面将展示一张饼状图。


1.jpg


MAT 计算对象占据内存的两种方式。第一种是 Shallow heap,指的是对象自身所占据的内存。第二种是 Retained heap,指的是当对象不再被引用时,垃圾回收器所能回收的总内存,包括对象自身所占据的内存,以及仅能够通过该对象引用到的其他对象所占据的内存。上面的饼状图便是基于 Retained heap 的。


可以看出,有个应用占用了大部分的堆内存,那么该应用很可能存在问题,我们继续往下看。


点击上图下方 Reports 中的 Leak Suspects,得到如下显示。


2.jpg


从描述上看到,主线程有个本地变量占用了很大内存,这个变量是 HashMap 的实例。

MAT 包括了两个比较重要的视图,分别是直方图(histogram)和支配树(dominator tree)。


直方图(histogram)


3.jpg


相较于 jmap 的-histo 命令,MAT 工具更加强大,除了能够展示各个类的实例数目以及这些实例的 Shallow heap 总和,还可以计算 Retained heap,并支持基于实例数目或 Retained heap 的排序方式(默认为 Shallow heap)。此外,MAT 还可以将直方图中的类按照超类、类加载器或者包名分组。


根据上图可知,char[] 占用内存最大,其次是 String,而我们知道 String 里用 char[] 存储数据,所以归根结底还是 String 对象占用内存过大。我们之间来看 String 是被谁引用的,选中那一行,右键点击 List objects。


  • with incoming references 表示的是当前查看的对象,被外部应用
  • with outGoing references 表示的是当前对象,引用了外部对象


4.jpg

5.jpg


结果发现是 Goods 对象的 types 属性消耗了大量内存。


支配树(dominator tree)


支配树的概念源自图论。在一则流图(flow diagram)中,如果从入口节点到 b 节点的所有路径都要经过 a 节点,那么 a 支配(dominate)b。在 a 支配 b,且 a 不同于 b 的情况下(即 a 严格支配 b),如果从 a 节点到 b 节点的所有路径中不存在支配 b 的其他节点,那么 a 直接支配(immediate dominate)b。这里的支配树指的便是由节点的直接支配节点所组成的树状结构。


MAT 将按照每个对象 Retained heap 的大小排列该支配树。如下图所示:


6.jpg


当在支配树视图中选中某一对象时,我们还可以通过 Path To GC Roots 功能,反向列出该对象到 GC Roots 的引用路径。如下图所示:



7.jpg


8.jpg


总结


通过 jstat 命令可以监控 GC 情况,通过查看 GC 过程中的值变化,来预测危险的发生,及时进行 GC 优化。


如果真的遇到内存溢出,我们可以通过 MAT 工具来分析错误原因,定位到具体的代码,然后处理相关问题。

目录
相关文章
|
7月前
|
前端开发 JavaScript 算法
尚硅谷JVM全套教程
尚硅谷JVM全套教程
|
5月前
|
Arthas 监控 Java
(十一)JVM成神路之性能调优篇:GC调优、Arthas工具详解及各场景下线上最佳配置推荐
“在当前的互联网开发模式下,系统访问量日涨、并发暴增、线上瓶颈等各种性能问题纷涌而至,性能优化成为了现时代开发过程中炙手可热的名词,无论是在开发、面试过程中,性能优化都是一个常谈常新的话题”。
528 3
|
1月前
|
Arthas Prometheus 监控
监控堆外使用JVM工具
监控堆外使用JVM工具
39 7
|
2月前
|
JavaScript 前端开发 Java
jvm的jshell,学生的工具
本文介绍了JVM的jshell工具,它为Java平台添加了REPL(读取-评估-打印循环)功能,使得学习、探索编码和原型代码变得更加便捷,但作者认为其在实际开发中较为鸡肋。
46 1
jvm的jshell,学生的工具
|
2月前
|
Arthas 监控 数据可视化
JVM进阶调优系列(7)JVM调优监控必备命令、工具集合|实用干货
本文介绍了JVM调优监控命令及其应用,包括JDK自带工具如jps、jinfo、jstat、jstack、jmap、jhat等,以及第三方工具如Arthas、GCeasy、MAT、GCViewer等。通过这些工具,可以有效监控和优化JVM性能,解决内存泄漏、线程死锁等问题,提高系统稳定性。文章还提供了详细的命令示例和应用场景,帮助读者更好地理解和使用这些工具。
|
2月前
|
存储 监控 算法
JVM调优深度剖析:内存模型、垃圾收集、工具与实战
【10月更文挑战第9天】在Java开发领域,Java虚拟机(JVM)的性能调优是构建高性能、高并发系统不可或缺的一部分。作为一名资深架构师,深入理解JVM的内存模型、垃圾收集机制、调优工具及其实现原理,对于提升系统的整体性能和稳定性至关重要。本文将深入探讨这些内容,并提供针对单机几十万并发系统的JVM调优策略和Java代码示例。
63 2
|
2月前
|
小程序 Oracle Java
JVM知识体系学习一:JVM了解基础、java编译后class文件的类结构详解,class分析工具 javap 和 jclasslib 的使用
这篇文章是关于JVM基础知识的介绍,包括JVM的跨平台和跨语言特性、Class文件格式的详细解析,以及如何使用javap和jclasslib工具来分析Class文件。
60 0
JVM知识体系学习一:JVM了解基础、java编译后class文件的类结构详解,class分析工具 javap 和 jclasslib 的使用
|
4月前
|
缓存 监控 算法
吃透 JVM 诊断方法与工具使用
【8月更文挑战第4天】深入了解并掌握JVM诊断需把握几大要点:1) 熟悉JVM内存模型,如堆、栈及方法区;2) 掌握垃圾回收机制与算法;3) 运用工具如`jps`(查看Java进程)、`jstat`(监控运行状态)、`jmap`(生成堆快照)、`jhat`(分析堆快照)、`jstack`(检查线程栈); 4) 利用专业工具如Eclipse Memory Analyzer分析堆转储文件查找内存泄漏; 5) 动态监控与调整JVM参数; 6) 结合日志分析性能瓶颈。通过实战案例加深理解,有效应对JVM性能问题。
|
5月前
|
运维 监控 Java
(十)JVM成神路之线上故障排查、性能监控工具分析及各线上问题排错实战
经过前述九章的JVM知识学习后,咱们对于JVM的整体知识体系已经有了全面的认知。但前面的章节中,更多的是停留在理论上进行阐述,而本章节中则更多的会分析JVM的实战操作。
135 1
|
5月前
|
监控 Java 开发者
Java面试题:如何使用JVM工具(如jconsole, jstack, jmap)来分析内存使用情况?
Java面试题:如何使用JVM工具(如jconsole, jstack, jmap)来分析内存使用情况?
232 2