记录一次项目OOM内存飙升的问题
碰到一个问题,现场医院的接口服务经常因为内存满的原因导致容器自动重启。这边k8s容器重启后会清除历史日志,所以也看不到错误信息。新到公司咱也不知道docker编排文件为啥这样写,只能靠自己逐步分析了。看监控图里的内存使用量,每个机器是2G,使用量逐步上升最终导致容器崩溃重启。定位问题首先想到是mysql的问题,vpn登录跳板机连接现场数据库,查看对应库的运行情况。通过SELECT * FROM i
碰到一个问题,现场医院的接口服务经常因为内存满的原因导致容器自动重启。这边监控容器重启后会清除历史日志,所以也看不到错误信息。新到公司咱也不知道docker编排文件为啥这样写,只能靠自己逐步分析了。
看监控图里的内存使用量,每个机器是2G,使用量逐步上升最终导致容器崩溃重启。
定位问题
首先想到是mysql的问题,vpn登录跳板机连接现场数据库,查看对应库的运行情况。
通过SELECT * FROM information_schema.INNODB_TRX来查看事务运行情况,发现有很多RUNING正在运行中状态的事务,拿到事务id手动一个个kill杀掉,服务恢复正常。
但这样只是治标不治本,总不能每次内存满了都人工手动杀吧。
后面连接现场服务环境,准备去看对应GC垃圾回收信息。
先是通过top | grep java找到项目进程id(因为是单机grep java,如果是多服务检索项目名更好),再top -Hp pid查找该进程下线程运行情况,输出大写的P查看线程占用cpu最高的或者通过大写M查看内存占用最高的线程,拿到线程id,再printf "%x\n" sid得到对应十六进制值,通过jstack PID|grep [SID(线程号十六进制)] -A 10查看堆栈日志信息。(jstack -F pid好像能直接查看挂死的线程)
java.lang.Thread.State: TIMED_WAITING (parking)爆了很多这个信息,定位是线程池的问题,创建了很多线程并阻塞占用了内存资源。
进项目代码排查,发现是有个@Async异步任务的问题。
看代码后发现,原来是之前人写的功能,执行任务时没有指定线程池的缘故,这就产生了两个很大的问题:
1.看@Async源码,未指定线程池情况下,默认线程数只有1。
2.未指定线程池,默认使用的线程池是SimpleAsyncTaskExecutor
。我们不看这个类的源码,只看它上面的文档注释,如下:
主要说了三点
-
为每个任务新起一个线程
-
默认线程数不做限制
-
不复用线程
就这三点,只要任务耗时长一点,服务器就给你来个OOM
。
解决方法:使用自定义的线程池, 加入如下代码后问题解决。
更多推荐
所有评论(0)