Memory Analyzer使用教程

2,176 阅读2分钟

前言

  • 最近线上系统基本安然无事。本着没事找事的原则,就找运维打了份线上GC的dump文件,用来分析学习一波。然后就接触到了Memory Analyzer分析工具

下载安装

1647762408(1).png

构建dump文件

  • 本次示例就自建生成一个Java版dump文件,先构造一个实体对象:
@Data
public class User {
    private String name;
    private String phone;
}
  • 构造一个main方法,里面遍历向集合中放入实体对象:
public class Demo1 {

    public static void main(String[] args) {
        List<User> list = new ArrayList<>();
        for (int i = 0; i < 10000000; i++) {
            User user = new User();
            user.setName("小猪");
            user.setPhone("15222222222");
            list.add(user);
        }
        System.out.println("添加完成");
    }
}
  • 然后添加启动JVM参数打印GC日志和输出dump文件:
-Xms10m -Xmx20m -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=F:\hporf
  • 最后启动运行main方法后,再控制台会打印GC日志也会生成dump文件:
[0.002s][warning][gc] -XX:+PrintGCDetails is deprecated. Will use -Xlog:gc* instead.
[0.006s][info   ][gc] Using G1
[0.007s][info   ][gc,init] Version: 17.0.2+8-LTS-86 (release)
[0.007s][info   ][gc,init] CPUs: 16 total, 16 available
[0.007s][info   ][gc,init] Memory: 16291M
[0.007s][info   ][gc,init] Large Page Support: Disabled
[0.007s][info   ][gc,init] NUMA Support: Disabled
[0.007s][info   ][gc,init] Compressed Oops: Enabled (32-bit)
[0.007s][info   ][gc,init] Heap Region Size: 1M
[0.007s][info   ][gc,init] Heap Min Capacity: 10M
[0.007s][info   ][gc,init] Heap Initial Capacity: 10M
[0.007s][info   ][gc,init] Heap Max Capacity: 20M
[0.007s][info   ][gc,init] Pre-touch: Disabled
[0.007s][info   ][gc,init] Parallel Workers: 13
[0.007s][info   ][gc,init] Concurrent Workers: 3
[0.007s][info   ][gc,init] Concurrent Refinement Workers: 13
[0.007s][info   ][gc,init] Periodic GC: Disabled
....
[0.247s][info   ][gc,ergo        ] Attempting maximum full compaction clearing soft references
[0.247s][info   ][gc,task        ] GC(29) Using 2 workers of 13 for full compaction
[0.247s][info   ][gc,start       ] GC(29) Pause Full (G1 Compaction Pause)
[0.247s][info   ][gc,phases,start] GC(29) Phase 1: Mark live objects
[0.261s][info   ][gc,phases      ] GC(29) Phase 1: Mark live objects 13.960ms
[0.261s][info   ][gc,phases,start] GC(29) Phase 2: Prepare for compaction
[0.263s][info   ][gc,phases      ] GC(29) Phase 2: Prepare for compaction 1.940ms
[0.263s][info   ][gc,phases,start] GC(29) Phase 3: Adjust pointers
[0.268s][info   ][gc,phases      ] GC(29) Phase 3: Adjust pointers 4.387ms
[0.268s][info   ][gc,phases,start] GC(29) Phase 4: Compact heap
[0.270s][info   ][gc,phases      ] GC(29) Phase 4: Compact heap 2.186ms
[0.270s][info   ][gc,heap        ] GC(29) Eden regions: 0->0(1)
[0.270s][info   ][gc,heap        ] GC(29) Survivor regions: 0->0(1)
[0.270s][info   ][gc,heap        ] GC(29) Old regions: 16->16
[0.270s][info   ][gc,heap        ] GC(29) Archive regions: 0->0
[0.270s][info   ][gc,heap        ] GC(29) Humongous regions: 3->3
[0.270s][info   ][gc,metaspace   ] GC(29) Metaspace: 775K(896K)->775K(896K) NonClass: 716K(768K)->716K(768K) Class: 59K(128K)->59K(128K)
[0.270s][info   ][gc             ] GC(29) Pause Full (G1 Compaction Pause) 18M->18M(20M) 22.807ms
[0.270s][info   ][gc,cpu         ] GC(29) User=0.03s Sys=0.00s Real=0.02s
java.lang.OutOfMemoryError: Java heap space
[0.270s][info   ][gc,marking     ] GC(25) Concurrent Mark Abort
[0.270s][info   ][gc             ] GC(25) Concurrent Mark Cycle 55.477ms
Dumping heap to F:\hporf\java_pid14460.hprof ...
Heap dump file created [31773704 bytes in 0.048 secs]
[0.319s][info   ][gc,heap,exit   ] Heap
[0.320s][info   ][gc,heap,exit   ]  garbage-first heap   total 20480K, used 18573K [0x00000000fec00000, 0x0000000100000000)[0.320s][info   ][gc,heap,exit   ]   region size 1024K, 1 young (1024K), 0 survivors (0K)
[0.320s][info   ][gc,heap,exit   ]  Metaspace       used 787K, committed 960K, reserved 1056768K
[0.320s][info   ][gc,heap,exit   ]   class space    used 60K, committed 128K, reserved 1048576K
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at java.base/java.util.Arrays.copyOf(Arrays.java:3512)
	at java.base/java.util.Arrays.copyOf(Arrays.java:3481)
	at java.base/java.util.ArrayList.grow(ArrayList.java:237)
	at java.base/java.util.ArrayList.grow(ArrayList.java:244)
	at java.base/java.util.ArrayList.add(ArrayList.java:454)
	at java.base/java.util.ArrayList.add(ArrayList.java:467)
	at com.zrh.web.demo.Demo1.main(Demo1.java:14)
  • 上述的GC日志就不在本篇文章解析了,读者可以自行下去解析,以下是生成的dump文件。 1647764002(1).png

MemoryAnalyzer解析

  • 点击进入MemoryAnalyzer可视界面后,选择点击File,点击Open Heap Dump...,选择刚才生成的dump文件:

1647764837(1).png

  • Details:表示当前通过dump文件解析出来的堆得基本信息;
  • Biggest Objects by Retained Size:表示内存使用较大线程信息;
  • Histogram:表示当前内存中每个类的实例数:

1647765393(1).png

  • Dominator Tree:表示排列出最大的对象以及其所占百分比(堆查找内存溢出很有帮助):

1647765582(1).png

  • 通过上述的几个列表基本也可以推断出内存溢出的原因,就是main线程里创建了较多的com.zrh.web.demo.User对象;

  • 除了通过上述的数据指标可以让开发人员预估问题的所在外,MemoryAnalyzer也已经帮开发人员预估的内存问题的可能原因:

1647766556(1).png

  • 然后点击Details,选择list objects -- with outgoing references:(查看这个对象持有的外部对象引用)

1647766914(1).png

  • 可以找到在list集合中存在50万个对象,就是最后导致系统内存溢出原因。

最后

  • 虚心学习,共同进步-_-