MAT介绍
MAT是Memory Analyzer tool的缩写,是一种快速,功能丰富的Java堆分析工具,能帮助你查找内存泄漏和减少内存消耗。很多情况下,我们需要处理测试提供的hprof文件,分析内存相关问题,那么MAT也绝对是不二之选。
下载地址:Eclipse Memory Analyzer Open Source Project | The Eclipse Foundation
演示OOM
因为MAT是对java堆内存快照进行分析的工具,拿到内存快照文件的方法如下:
- jmap -dump:live,format=b,file=dump.hprof PID 使用jmap命令拿到给定PID的jvm进程的堆内存,生产、本地环境均可使用。
- -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=dumpPath 生产可使用的方法,添加这两个jvm参数,一旦jvm发生OOM的时候,会自动生成jvm堆内存dump文件。非常建议生产加上这两个jvm参数。
- jvisualvm工具,该工具是jdk自带的工具,也可以拿到dump文件,一般本地测试使用。
下面我演示第二种方法:
先设置jvm参数:为方便演示oom的出现,我设置堆内存大小最大为50M,oom发生时会把自动生成的dump文件存放在C:\Users\MSI-\Desktop\1
路径下。
-Xmx50M -Xms50M -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=C:\Users\MSI-\Desktop\1
代码:
这里有一个启动类,启动类启动时,会在list中存入50000个UUID,因为list是static修饰的,所以该list有GC ROOTS,即便发生full gc,也不会回收该对象。
还有一个DumpController,当请求接口时,会往成员变量list中塞入50000个UUID,因为DumpController长期存在,list也有GC ROOTS,gc时也不会回收该对象。
@SpringBootApplication
@ConfigurationPropertiesScan
public class MinioApplication {
static List<String> list = new ArrayList<>();
public static void main(String[] args) {
for (int i = 0; i < 50000; i++) {
list.add(UUID.randomUUID().toString());
}
SpringApplication.run(MinioApplication.class, args);
}
}
@RestController
@RequestMapping("/dump")
public class DumpController {
ArrayList<String> list = new ArrayList<>();
@GetMapping("/test")
public String test() {
for (int i = 0; i < 50000; i++) {
list.add(UUID.randomUUID().toString());
}
return "ok";
}
}
下面,进行测试:
调用:http://localhost:8080/dump/test
调用几次后,发现直接卡死。控制台输出错误:
抛出一个Error:java.lang.OutOfMemoryError:GC overhead limit exceeded GC
回收时间过长时会抛出该异常,过长的定义是,超过98%的时间用来做GC并且回收了不到2%的堆内存。
先用jvisualvm工具分析堆内存占用情况:
这里发现整个老年代和伊甸园区都被占满,并且上面分析过main方法中的list和DumpController中的list都无法被gc回收,导致了OOM,这时候已经无法创建新的对象存放到堆中。
MAT分析内存占用
上面配置过dump文件的生成路径,去该路径下看,确实生成了一个hprof,全名是heap profile,译为堆快照文件。
使用MAT打开该文件,选择Open a Heap Dump
选择Leak Suspects Report,这样MAT就会向用户报告可能发生内存泄漏的地方:
点击首页,会有一个饼状图,直接就显示了有两个可能发生内存泄漏的地方:一个占用29.6M,一个占用5.6M
下面有详细信息:明确指出两个地方,一个是主启动类,一个是DumpController。与我们分析一致。
分析内存快照,说白了,无非就是找到占用内存最大的对象,然后就是找到谁在引用这个对象,是哪个线程在引用,接着找到创建这些对象的相关代码和方法,然后你就可以一头扎到对应的源码里去分析问题了。
点击dominator_tree,这里就会按内存占用大小的降序排列出jvm中的对象。这里可以看到前两个就是DumpController和主启动类,一个30M,一个5M多。
包括对象中各个成员变量:
找到占用内存最大的对象之后,最后一步就是要定位一下是哪一行代码,或者是哪个方法创建了那么多的对象?
这个又需要另外一个视图了,点击下图的红圈处,会进入thread_overview界面,这里会展示出来JVM中所有的线程以及每个线程当时的方法调用栈帧,以及每个方法中创建了哪些对象:
标签:MAT,dump,占用,list,内存,jvm From: https://www.cnblogs.com/wwjj4811/p/17132854.html