dump文件分析OOM及线程堆栈
OutOfMemoryError (OOM)
如果项目报错: OutOfMemoryError: Java heap space,说明堆内存空间(Heap Space)中没有足够的空间来分配对象了。
一旦发生 OOM,系统有可能不可用,或者频繁重启。属于非常严重的问题。
OOM 的解决方法一:
比较简单粗暴的一种做法,是增大堆内存空间。
在 项目启动时,调大 -Xms 和 -Xmx 。
-Xms 参数用于设置JVM的初始堆内存大小,而-Xmx参数用于设置JVM的最大堆内存大小。
-Xms1024m -Xmx2048m
OOM 的解决方法二:
-Xms 和 -Xmx 哪怕调大了,如果没有找到根本的原因,大对象不断创建,不断地占用堆内存空间,还是有可能会继续 OOM。
可以设置在发生 OOM 时自动 dump 文件,保存到指定的文件。
HeapDumpPath就是指定的文件路径,最好设置一下,免得OOM了找不到文件。
dump文件是进程的内存镜像,可以通过分析 dump 文件,找到导致 OOM 的代码。
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/dumpfile/heapdump.hprof
oom 代码示例:
不断循环,不断创建对象,占用堆内存空间,模拟 OOM 的场景。
public class OomTest {
public static void main(String[] args) {
createObject();
}
/**
* 不断循环,不断创建对象,模拟 OOM 的场景
*
*/
public static void createObject() {
List<OomObject> oomList =new ArrayList<>();
while (true) {
OomObject oomObject = new OomObject();
oomList.add(oomObject);
}
}
}
添加 jvm 相关启动参数( Vm option ) :
-
点击 Edit Configurations:
-
点击 Modify options, 选择 Add Vm Options :
启动参数( Vm option ) 如下:
添加启动参数,设置最大内存为 8m,模拟 OOM 的场景。
HeapDumpOnOutOfMemoryError 表示在发生 OOM 时会自动 dump 。
-Xms4m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:/heapdump.hprof
运行代码:
结果如下:
Connected to the target VM, address: '127.0.0.1:63909', transport: 'socket'
java.lang.OutOfMemoryError: Java heap space
Dumping heap to D:/heapdump.hprof ...
Heap dump file created [31923817 bytes in 0.263 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3210)
at java.util.Arrays.copyOf(Arrays.java:3181)
at java.util.ArrayList.grow(ArrayList.java:261)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227)
at java.util.ArrayList.add(ArrayList.java:458)
at com.example.demo.oom.OomTest.createObject(OomTest.java:21)
at com.example.demo.oom.OomTest.main(OomTest.java:14)
可以看到 :
java.lang.OutOfMemoryError: Java heap space
Dumping heap to D:/heapdump.hprof ...
这里的 D:/heapdump.hprof ,就是 dump 文件保存的路径。
打开 dump 文件,分析大对象
点击 Profiler ,再点击 Open Snapshot ,选择 上面的 dump 文件 D:/heapdump.hprof 。
点击 Biggest Objects, 接着再点击 Calculate retained size and biggest objects.
可以看到 OomObject 这个对象占用了最大的内存。
生产环境的 dump 文件,一般都会很大,好几G 都有可能。要等多一会 。
结果如下:
Shallow 和 Retained 分别表示对象自身不包含引用的大小和对象自身并包含引用的大小。
在实际的情景中,往往会有多个对象及多个引用。
比如:在大对象中,对象A 占用的内存最大,
那么再看对象A中包含了哪些对象,比如 对象A包含了 A1,A2,A3,这里面A1占用的内存最大,
再看一下内存最大的 A1中包含了哪些对象,逐步深入。
如下图所示:
TaskThread 占用的内存最大,展开后发现 TaskThread 包含的内存最大,逐步深入,
就能找到导致oom的大对象。然后再搜索代码中的大对象。
使用 jdk 自带的 java visualvm ,分析dump文件, 找到 OOM 的代码
另外,也可以下载 MAT(Memory Analyzer Tool) 来分析 dump 文件。
此处使用 jdk 自带的 java visualvm 来分析 dump 文件, 找到 导致OOM 的代码 。
在 jdk 的 bin 文件中,找到 jvisualvm.exe 。点击即可运行。
- 装入 hprof 后缀的 dump 文件 :
打开 VisualVM 后,点击左上角的图标 ,装入, 文件名选择 之前的 dump 文件 D:/heapdump.hprof 。
文件类型 选择 【堆Dump(*.hprof)】。
可以看到,在出现 OutOfMemoryError 异常错误时进行了堆转储,导致 OutOfMemoryError 异常错误的线程 是哪一个。
点击这个线程,即可显示导致OOM的线程堆栈。
生产环境的 dump 文件,一般都会很大,好几G 都有可能。要等多一会 。
- 可以看到OOM的具体代码是类的哪一行:
这个分析出来的代码,是OOM发生时的代码,并不一定是导致OOM的代码。
最好结合上面分析出来的 大对象,一起排查。
以占用过多内存的大对象为准。
大对象占据过多的内存,就会导致某一次请求时OOM。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步