如何分析 JVM OOM 内存溢出 Dump 快照日志
文章目录
- 1、需求背景
- 2、OOM 触发
- 3、Dump 日志分析
1、需求背景
企业开发过程中,如果系统服务客户量比较大,偶尔会出现OOM内存溢出
问题,导致服务发生宕机,停止对外提供访问。
这种情况就需要排查定位内存溢出的原因(总不能任由他宕机),不过默认情况JVM内存溢出导致宕机并不会生成dump快照
,开发就很难找到本次内存溢出的原因。
这就需要我们在JAR包
启动命令中添加一些参数,例如:-XX:+HeapDumpOnOutOfMemoryError
,这样就会生成对应OOM Dump
快照,方便研发排查内存泄露原因,参数如下:
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/applog
2、OOM 触发
本次演示,我通过Docker
方式来启动Java进程,所以需要先构建Docker镜像,我这里使用Dockerfile
来完成镜像构建:
# 基于JDK8基础镜像
FROM openjdk:8
# 容器中创建 applog 目录(存放dump文件)
RUN mkdir -p /applog
# 将宿主机jar添加到容器中
ADD MyProject-0.0.1-SNAPSHOT.jar /MyProject-0.0.1-SNAPSHOT.jar
# 配置:生成HeapDump快照日志
ENV JAVA_OPTS="-Xms256M -Xmx256M -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/applog"
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar /MyProject-0.0.1-SNAPSHOT.jar"]
然后使用命令进行镜像文件构建并运行容器:
# 构建镜像
docker build -f Dockerfile -t oom-project .
# 启动容器
docker run -d -p8080:8080 oom-project
上述构建配置文件,如果没有JAVA_OPTS
参数配置,即便是发生OOM也不会生成对应Dump文件。基于上述配置构建出镜像后,就可以完成服务启动,然后在浏览器上输入URL完成接口调用,触发接口调用,大概过了几秒钟就出现了OOM内存泄露问题,如下图:
3、Dump 日志分析
由于上面触发了OOM,那么就会在Docker容器内部的 applog 文件夹中可以看到 dump 文件了:
# 进入Docker容器内部
docker exec -it 容器id bash
进入到 applog 目录,可以看到对应的 dump 文件:java_pid7.hprof
此时,就可以把容器内的dump
快照文件拷贝到宿主机当中:
docker cp 容器id:/applog/java_pid7.hprof /opt
接着,就可以将生成的 OOM dump
快照文件下载到电脑本地文件夹中:
最后,就要用分析工具进行分析了,现在开源了很多 Dump 分析工具,例如:Eclipse MAT
,不过我个人不习惯使用MAT(还得单独安装),不如直接通过IDEA
自带工具进行分析,直接把该文件拖入到IDEA界面中,就可以看到,存在大对象导致内存溢出:
接着看一下具体是哪个类、哪行代码出现了问题:OOMController类的21行
最后看一眼代码:原来是/oom接口中,存在死循环不断向List中添加对象,最后导致Heap内存发生了OOM现象: