Tomcat 多模块部署时,一次 CPU 爆仓问题排查记录

背景

产品新一轮压力测试,这几天不知跑了什么任务,后台测试环境 CPU 超高导致服务不可用,记录一下跟踪的过程。

定位进程

top 查看进程 CPU 超高的是 Tomcat 进程:
在这里插入图片描述
查看对应线程
在这里插入图片描述
打印线程编号:

printf  "%x" 7517
jstack 7492 | grep '1d5d'

堆栈信息显示这个线程是 GC 线程,而且全部的 GC 线程都在工作,是典型的 GC 导致的 “Stop the world” 后遗症。
在这里插入图片描述

定位问题模块

查看日志,容器日志中的内存溢出异常:

Exception: java.lang.OutOfMemoryError thrown from the 
UncaughtExceptionHandler in thread "quartzScheduler_QuartzSchedulerThread"

由于 Tomcat 的 webapps 目录下有多个模块,而容器日志的异常并不全,无法定位具体的模块。可以确定的是凌晨 CPU 超高时执行 Quartz 定时任务的有两个模块,猜测是距离内存溢出最近的应用,就先移除另一个应用,继续监控。

监控一阵儿后,测试部需要继续测试,就把另一个模块先还原回去了,并顺带监控了下它的日志。Quartz 触发了上次未执行的定时任务

[org.springframework.scheduling.quartz.LocalDataSourceJobStore] -
 localhost-startStop-2 - 
 Handling 2 trigger(s) that missed their scheduled fire-time

后面接着就是报错的任务日志:

ERROR [org.quartz.core.JobRunShell] - DefaultQuartzScheduler_Worker-1 - xx.xx 
threw an unhandled Exception: 
java.lang.OutOfMemoryError: GC overhead limit exceeded
	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 XX.xx(XXX.java:xx)

这次打印的异常比较完整,可以定位到具体的错误,根源是代码缺陷,一次从 ES 中查询了过多的数据到内存,导致内存溢出。

之前已经调整过 JVM 参数,最大堆内存为 3G ,再优化参数,添加 OOM 导出配置:

-XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/xx.dump

启示录

OOM 时 dump 日志文件也是够大的,超过 3G,这破网也没下载成功过。

用 jmap 打印堆数据,占据内存最大的是 char[] 对象,工程中有大量的字符串拼接操作,但压死内存的最后一根稻草是大对象,但是优化字符串拼接也是必须的,毕竟是一个包含多模块的大工程。

多模块开发,而且每个模块都用了 SpringBoot ,还是有点坑的。除了部署时重复引用的 jar 包导致的磁盘资源浪费外,还有 JVM 内存资源的消耗,每个应用的类加载器都要加载自己的 jar 包,这个 OOM 就是潜在的威胁!

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 代码科技 设计师:Amelia_0503 返回首页
实付 9.90元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值