一,代码模拟 OOM 异常
示例代码:
import java.util.ArrayList;
import java.util.List;
public class OomDemo {
public static void main(String[] args) {
List<User> list = new ArrayList<>();
while (true) {
User user = new User();
user.setId(RandomUtil.randomLong(6));
user.setName(RandomUtil.randomString(8));
list.add(user);
}
}
@Data
public static class User {
private Long id;
private String name;
}
}
修改 jvm 参数,发送 OOM 异常时设置自动保存 堆转储快照文件
快照:
-Xms
: 初始堆大小。默认 (MaxHeapFreeRatio 参数可以调整) 空余堆内存小于 40%时,JVM就会增大堆直到 -Xmx 的最大限制。
-Xmx
: 最大堆大小。默认 (MaxHeapFreeRatio 参数可以调整) 空余堆内存大于 70% 时,JVM会减少堆直到 -Xms 的最小限制。
-XX:+HeapDumpOnOutOfMemoryError
:表示当 JVM 发生 OOM 时,自动生成 DUMP 文件 最重要的参数。
-XX:HeapDumpPath
:(可选)表示生成 DUMP 文件的路径, 可以指定文件名称, -XX:HeapDumpPath=${目录}/java_heapdump.hprof。若不指定文件名,默认为:java_pid_date_time_heapDump.hprof
-XX:OnOutOfMemoryError
:(可选)表示 OOM 时 需要触发的脚本,比如 重启动作,发送告警等等。
-Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=d:/
运行代码,生成 OOM 快照文件:
java.lang.OutOfMemoryError: GC overhead limit exceeded
Dumping heap to d:/\java_pid12004.hprof ...
Heap dump file created [12383540 bytes in 0.061 secs]
Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.CommandLineWrapper.main(CommandLineWrapper.java:63)
Caused by: java.lang.OutOfMemoryError: GC overhead limit exceeded
at com.ym.jvm.OomDemo.main(OomDemo.java:14)
... 5 more
到快照输出路径查看堆转储快照文件,用 MAT 工具打开分析。MAT 工具下载
二,内存泄漏分析
2.1,导入堆转储快照文件,自动生成分析报告
加载完成后,会弹出分析报告:
点击详情可以看到更加详细的信息:
从分析报告中,我们可以看到内存占用过大的对象为 User 对象,被 ArrayList 数组引用,还能通过堆栈信息定位到具体的代码位置!
若分析报告查看不到有用信息,通过下面方法进行查询详细信息:
2.2,查看占用过大对象
可以通过 histogram 来进行查询,一般是按照占用的内存倒序进行排序的。
可以看到 OomDemo$User 对象占用非常大的堆空间。
2.3,查看对象被谁引用
可以通过 dominator_tree 分析对象的调用链。
展开 占用内存最多的线程查看,可以看到 User 对象被 ArrayList 引用,所在线程为主线程,所在类的位置为 OomDemo。
2.4,查看堆栈信息定位代码
可以通过 thread_overview 查看方法的调用链信息和堆栈信息。
展开占用内存过大的问题线程,可以看到问题代码位置。
评论区