目录
1 模拟内存溢出程序
1.1 jvm配置
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:./logs/gc.log -Xms32m -Xmx32m -XX:MetaspaceSize=32m -XX:+UseParallelGC -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./dump
1.2 测试代码
- private Executor executor = Executors.newFixedThreadPool(5);
-
- private Random random = new Random();
- @Test
- public void oomTest() throws InterruptedException {
- while (true) {
- Employee employee = new Employee(random.nextInt(100), UUID.randomUUID().toString(),
- random.nextInt(100), new BigDecimal(random.nextDouble()));
- executor.execute(() -> execute(employee));
- }
- }
-
- private void execute(Employee employee) {
- try {
- System.out.println(employee);
- Thread.sleep(5000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
2 MAT工具进行内存分析
执行上述程序后,会dump出一个文件,用MAT打开文件即可
2.1 大纲介绍
查看大纲,可以直观看到:
总内存26.6M,大对象占用内存25.3M,以及有问题的那个类:ThreadPoolExecutor,和左侧工具栏的对象属性
2.2 Histogram视图介绍
堆内所有类的统计信息,包含类的实例数量和占用的空间。如果此处包含了自己的类就需要注意是否此类创建过多。默认的大小单位是 Bytes,可以在 Window – Preferences—Memory Analyzer-- 菜单中设置单位。笔者设置的是MB
- Shallow Size:对象自身占用的内存大小,不包括它引用的对象
- Retained Size:被GC后Heap上释放的内存大小,即当前对象大小+当前对象可直接或间接引用到的对象的大小总和
- out going:查看对象为什么耗内存,我们看到一个线程池占用了>25mb的内存,右键->list objects -> with outgoing references
可以看到线程池中的工作队列占用内存比较大
- in going:查看对象被谁引用,右键->list objects -> with ingoing references
这里我们可以清楚地定位到项目里具体哪个类引用到了该类,这里基本上可以分析出问题的原因了
2.3 Leak Suspects视图介绍
该视图会分析出可能发生内存泄漏的点,这里可以看出一个java.util.concurrent.ThreadPoolExecutor实例被系统类加载器加载,占用了94.86%的空间。该实例呗com.demo.mianshi.OOMTest的一个对象引用
我们点开detail信息
- Shortest Paths To the Accumulation Point:累积点的最短路径
可以看到线程:0xfe67d1d0累积了一个java.util.concurrent.ThreadPoolExecutor,这个视图类似于Histogram视图的in going
- Accumulated Objects in Dominator Tree:累积对象所在树
可以看到该对象持有的任务队列占用内存比较大
2.4 Dominator Tree
列出了堆中的对象,并且该对象的其他对象。和Leak Suspects视图中Accumulated Objects in Dominator Tree类似
当然,MAT还有一些其他的分析报告,这里就不一一列举了,常用的就这些,平时工作中定位问题基本上已经足够了。