现象描述:
用户在进行报表下载过程中,每隔几天系统就会报错500,导致无法进行报表下载。
原因分析:
通过查看系统log日志以及gc日志,发现是因为下载报表过程中JVM频繁进行Full GC,而且老年代的堆内存不断增加且无法回收,最终导致OOM。
问题排查:
- 审查代码,发现存在很多共用的变量位于for循环之中
- 出现OOM可能存在如下问题
- JVM堆内存设置偏小
- 程序中存在大对象无法回收
解决方案:
- 将公共变量提取到for循环外面共用(可以减少对象创建,但不是导致OOM的核心)
- a. 将原先内存扩大一倍(系统正常使用时长比原先多几天,但最终仍会报OOM)
b. 通过工具分析堆内存快照,定位到代码中存在大对象
排查中核心过程:
查看项目日志文件
应用日志文件:显示Java Heap OutOfMemory错误
GC日志文件:新生代/老年代内存均占满,且进行Full GC内存无变化,导致OOM
导出堆快照Dump.hprof二进制文件
注意: 使用jdk自带的jps和jmap命令(jdk必须是完整的,不能是简化版的,即jdk/bin下有jps和jmap等,否则不支持相关命令)
1.jps –l该命令显示本地所有的正在运行的服务信息,包括进程id和服务名称
2.使用该服务功能后,通过jmap -dump:file=C:/tmp/dump.hprof 4672 导出对应进程ID(4672)的堆快照文件到指定的位置(C:/tmp/)
3.使用Jprofiler工具分析堆内存快照文件
- 首先下载jprofiler工具到本地并安装成功
- 使用jprofiler打开此前生成的dump.hprof二进制文件
3.选择其中的Biggest Objects选项栏位,并从大到小排列显示堆内存中对象
4. 我们会发现workbook该对象大小占比55%,显然是个超大对象,因此我们需要检查该对象在执行完成后是否释放
修改代码
- 根据代码审查,发现该对象使用完后并未及时释放,导致该对象一致被引用且大小不断增大导致最终不断Full GC且内存无法释放,形成OOM现象。
- 在原有功能代码执行完成后调用workbook.close()方法释放对象引用
将修改后的代码打包上传到私服Nexus仓库
如下截图为通过STS工具将本地项目打jar包推送到nexus仓库中的流程
注意:maven命令为clean和deploy; jre需要选择本地的jdk目录
推送成功
如果没有执行mvn install指令,后续需要重新执行此命令,更新maven引用后其他引用此包的代码才不会报找不到该包下的方法错误