目录
一、事故现场
监控发现线上服务挂了一台,登录服务器查询日志发现了java.lang.OutOfMemoryError: GC overhead limit exceeded,该错误表示:jvm使用大量的时间去做gc,但没有什么效果(创建了大量大对象且不回收垃圾)。异常日志中可以看到引发OOM的原因是hibernate 调用repository.findAll()导致的
二、分析过程
依然是导出hprof文件到本地使用jdk自带的jvisualvm进行分析,这次没有那么幸运可以直接通过点击跳转“查看导致 OutOfMemoryError 异常错误的线程”查到有用的线索,那就来看看有什么大对象吧
可以看到这个list.size竟然有159482,展开看看这个list都存着什么对象。展开查看metadata——>fields——>fullName(或者tableName和columnName),看看有没有熟悉的表和字段可以定位到是查询了哪个表的全部数据
到这里,已经排查出是查询了部门表的全部数据,再结合该时间执行的定时任务,最终找到代码位置:List<SysDepartmentMainData> departments = departmentMainDataRepository.findAll();
但是问题来了,部门数据怎么可能有15万多?该项目中的部门表是定时任务每天先清空表再同步公司主数据部门信息全量覆盖的(该方法是加事务的,所以不会有清空表后但调用主数据接口失败导致表里没数据的情况),查看公司主数据的部门表只有4千多,那这15万是怎么进来的?那就要去看一下存表的代码逻辑了,果然发现了问题,以下是问题原因和最终修复
三、遗留问题
查看大对象list中存放的对象表名和字段名与实体不一致,像被截断了一样。希望有知道原因的大神可以解惑🙏
实体中 | 引用中 |
SysDepartmentMainData | sysdepartm0_ |
id | id1_53_ |
deptcode | deptcode8_53_ |
deptleaderid | deptlea10_53_ |
deptname | deptnam11_53_ |