本文为《深入学习 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 界面没反应,参考:https://www.eclipse.org/forums/index.php/t/1090889/,换个包即可。

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 exceededat 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 的主界面将展示一张饼状图。

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

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

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

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

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

直方图(histogram)

相较于 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 表示的是当前对象,引用了外部对象

结果发现是 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 的大小排列该支配树。如下图所示:

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

总结

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

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

推荐阅读

使用MAT命令行工具生成堆dump分析文件

JVM系列之:MAT工具使用教程相关推荐

  1. Eclipse安装Mat工具分析教程

    一.关于Mat MAT是Memory Analyzer的简称,它是一款功能强大的Java堆内存分析器.可以用于查找内存泄露以及查看内存消耗情况.MAT是基于Eclipse开发的,是一款免费的性能分析工 ...

  2. 正确使用 Adobe 系列全家桶工具的教程(2021.2.20)

    一.下载工具 https://wws.lanzous.com/inuXLlw5a1e 二.选择需要使用的产品,点击胶囊 三.找到安装目录下指定文件即可

  3. jvm系列(七):jvm调优-工具篇

    16年的时候花了一些时间整理了一些关于jvm的介绍文章,到现在回顾起来还是一些还没有补充全面,其中就包括如何利用工具来监控调优前后的性能变化.工具做为图形化界面来展示更能直观的发现问题,另一方面一些耗 ...

  4. JVM系列之:你知道Jhsdb整合的故障处理工具

    本文为<深入学习 JVM 系列>第二十一篇文章 Jhsdb 是 JDK9 引入的新的命令行工具,它有 clhsdb.debugd.hsdb.jstack.jmap.jinfo.jsnap ...

  5. Jvm 系列(八):Jvm 知识点总览

    对于Java程序员来讲,spring全家桶几乎可以搞定一切,spring全家桶便是精妙的招式,jvm就是内功心法很重要的一块,线上出现性能问题,jvm调优更是不可回避的问题.因此JVM基础知识对于高级 ...

  6. jvm系列(八):jvm知识点总览-高级Java工程师面试必备

    在江湖中要练就绝世武功必须内外兼备,精妙的招式和深厚的内功,武功的基础是内功.对于武功低(就像江南七怪)的人,招式更重要,因为他们不能靠内功直接去伤人,只能靠招式,利刃上优势来取胜了,但是练到高手之后 ...

  7. jvm系列(八):jvm知识点总览

    在江湖中要练就绝世武功必须内外兼备,精妙的招式和深厚的内功,武功的基础是内功.对于武功低(就像江南七怪)的人,招式更重要,因为他们不能靠内功直接去伤人,只能靠招式,利刃上优势来取胜了,但是练到高手之后 ...

  8. JVM常用参数与工具

    原文出处:http://www.cnblogs.com/zhguang/p/java-jvm-gc.html 目录 参数设置 收集器搭配 启动内存分配 监控工具和方法 调优方法 调优实例       ...

  9. JVM监控及诊断工具-GUI篇

    3.JVM监控及诊断工具-GUI篇 一.工具概述 使用上一章命令行工具或组合能帮您获取目标Java应用性能相关的基础信息,但它们存在下列局限: 1.无法获取方法级别的分析数据,如方法间的调用关系.各方 ...

最新文章

  1. 使用git更新github上的开源项目
  2. 反向非归零编码_【基础】什么是编码器?
  3. MXD文档保存和地图浏览
  4. python3安装步骤
  5. 五大经典算法之动态规划
  6. linux 邮件发送时间,Linux-No.04 Linux 设置定时任务发送邮件功能
  7. JFreeChart(八)之时序图
  8. c++怎么做app_怎么做一款app
  9. MFC小笔记:简单画图
  10. python \__call__
  11. java frame的使用方法_java内部窗体internalFrame的使用方法
  12. vs调试linux多线程,VS2017多线程调试
  13. 配置Typescript+Node环境
  14. [C] static和extern的作用
  15. python http通信接口开发
  16. 华为手机Root方法(推荐)
  17. 微型计算机硬件调研报告,计算机软硬件的产品调查报告分析.doc
  18. 直播开发软件平台搭建需要重点解决这一技术
  19. 计算机安装msvcr110.dll,安装WampServer报错 计算机中丢失MSVCR110.dll 的解决方法 | 睿客网...
  20. BTC 复制节点(节点复制)复制区块数据,实现快速同步区块数据

热门文章

  1. 涨握在线:美封堵华为5G,遭印企驳斥
  2. 2021-08-12 Android APP 保持屏幕常亮和取消屏幕常亮方法
  3. SpeedMent入门集成SpringBootStream常见操作
  4. ansible如何批量杀死nohup执行的脚本进程
  5. 测试设计点(微信红包、朋友圈点赞、视频播放、水杯、提现、筷子、登录)
  6. 当今社会22大流行骗术曝光-请大家警惕
  7. 在openssl申请证书时遇到的问题解决
  8. 国内开源镜像站点汇总[超级多]
  9. Java通过JfreeChart生成转Base64图片字符串(饼图、折线图、柱状图、折线图-多条、3D柱状图、气泡图、时序图、曲线图、区域图、分布图、联合分类图、双X轴图、K线图、柱状图-横向等图)
  10. InnoDB存储引擎内部结构