记录一次去年底的踩坑过程
起因介绍
业务系统正在稳当的跑着,结果半夜报警电话不停,机器load报警,起来看到cpu使用率很低,报警是由于内存导致的,非常奇怪本身系统的逻辑很简单,
就是保险系统的生单和出保逻辑对外提供一套dubbo的api服务。
逐层分析
按理推断即便系统有问题有也应该是业务系统应该报警才对,怎么会是机器在报警,为了验证自己的想法,
去看tomcat的gc日志,和自己预期一致,没有发现有fullgc, 系统日志也没有OOM异常,那就奇怪,4g内存,
jvm申请3g最大的堆内存,按道理机器不应该报内存不够啊,折腾了半天还是没有发现有什么异常的地方,
但是load的报警还在持续,回到刚刚的gc日志,忽然意识到一个问题居然一天之内一次fullgc都没发现,
很奇怪啊,按道理系统稳定运行一段时间后,fullgc应该是按照一个稳定的频率出现才对,
到服务器上sudo jstat -gcutil pid看了下gc统计日志,居然真是一次都木有啊
推理演变
把自己收集到的素材整理下:
- 系统没有
FullGC
- tomcat 内存足够
- 虚机load报警
这么说来推测一下,一定是有什么地方申请了一块内存但是没有释放,最起码是System.gc()没起效果
如果系统本身一直是在年轻代进行垃圾回收,那就意味着一定不会触发fullGC, 通过观察gc日志和对系统本身使用的
了解,我推断是由于使用了堆外内存导致的, 公司所有的中间件里面底层都是依赖netty来构建的,dubbo,asynchttp,
而系统本身就是犹如一个高速公路一样,只是做数据的传输工作,应用本身基本上没有本地缓存,而且强依赖外部接口(保险平台),
dubbo,asynchttp这些中间件的使用率非常频繁,
netty本身是会显式的调用System.gc()进行垃圾回收的,再联想到公司默认的jvm配置-XX:+DisableExplicitGC,这就不奇怪了,
本身如果系统自己有fullGC的话,那就是系统自身的fullGC来回收,但是这条路也不同,这就导致来堆外内存没有释放,
所以结论是: netty不停的申请堆外内存,而又得不到释放,导致系统load压力一致在涨
验证
-XX:+DisableExplicitGC
将这个参数去掉, 观察系统10分钟,sudo jstat -gcutil pid
发现有fullGC了, 系统的load慢慢也下来了
批评自己
其实上面的验证方法不太符合工程学实现的要求, 我们是通过推理来验证自己的想法, 还应该讲tomcat的gc dump出来,通过分析工具分析系统的哪一个线程和代码会有可能有问题,这才是比较科学的分析方法