记录一次生产问题的排查
第一天晚上
现象
1、前援反馈页面有接口陆续出现请求超时
2、登录后台服务器top命令查看发现java进程发生高cpu占用情况
3、查看对应业务日志,报数据库连接等待超时-数据库连接池连接无空闲
对应处理
1、临时调大数据库连接池最大连接数限制,从20调整到50
2、重启服务,先保证业务可用
第二天
观察服务器指标
1、free -h 查看内存使用情况,java进程占用高内存
2、查看进程gc情况,jstat -gc 2773947 1000 10,因为该项目没有打印GC日志只能这样临时观察。查看GC日志,
依次是
S0C:幸存0区容量(约350M)、
S1C:幸存1区容量(约350M)、
S0U:幸存0区已使用(201M)、
S1U:幸存1区已使用(0M因为是年轻代用-XX:+UseParNewGC,复制算法)
新生代内存按照8:1:1的比例分为一个eden区和两个survivor(survivor0,survivor1)区。一个Eden区,两个 Survivor区(一般而言)。大部分对象在Eden区中生成。回收时先将eden区存活对象复制到一个survivor0区,然后清空eden区,当这个survivor0区也存放满了时,则将eden区和survivor0区存活对象复制到另一个survivor1区,然后清空eden和这个survivor0区,此时survivor0区是空的,然后将survivor0区和survivor1区交换,即保持survivor1区为空, 如此往复
EC:伊甸区容量350M
EU:伊甸区已使用97M
OC:老年代容量4194M
OU:老年代已使用2878M
MC:元空间大小213M,jdk1.8之前叫永久代
MU:元空间已使用196M
CCSC:压缩类空间大小;(暂不知道用处)
CCSU:压缩类空间使用大小;(暂不知道用处)
YGC:年轻代gc 4786
FGC:老年代GC(可以看下图,这是两次GC日志的对比,才短短几分钟就出现了两次Full GC,而且查看上下两次GC情况,发现在FULL GC后垃圾有被回收,即EU和OU有变小,先排除内存泄漏。可以猜测应该是有大对象产生触发老年代空间担保机制,直接将老年代占满从而触发FULL GC。)
3、通过jmap -histo 18830| head -n 20 命令查看堆中比较多的对象
根据PaTerminalRealTimeQuery对象找到近期提交的代码,是漏掉一个设备id的参数,导致数据库中200w的设备数据都被捞到list中来。产生了大对象。
总结:主要是大对象的产生导致第一天的数据库连接被占用,第二天的内存过大、频繁FULL GC问题。
优化范式:
1、调整错误的业务代码
2、增加gc日志及dump文件的打印,具体参数改变如下
修改前:
-Xms5120M -Xmx5120M -Xmn1024M -XX:PermSize=256M -XX:MaxPermSize=512M -Xss256K -XX:SurvivorRatio=1 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 -XX:+CMSClassUnloadingEnabled -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 -XX:SoftRefLRUPolicyMSPerMB=0
修改后:
-Xms3072M -Xmx3072M -Xmn1024M -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=512M -Xss256K -XX:SurvivorRatio=1 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 -XX:+CMSClassUnloadingEnabled -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+PrintGCDetails -XX:+PrintHeapAtGC -XX:+PrintGCDateStamps -XX:+PrintTenuringDistribution -verbose:gc -Xloggc:/tysl/log/gc/manageLoadBalance-gc.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tysl/log/
修改点:
1、内存用不了这么大,反而老年代过大反而会让stw的时间边长,调小老年代内存
2、增加gc日志及dump的打印信息,以便后续排查问题
3、原项目估计是从1.7升级到1.8的,永久代的参数没有改过来,极端情况下,如遇到某些动态代理对象的生成会将元空间不断扩展(因为元空间不设置默认是共享整个物理内存空间的,极端场景会压缩堆空间,触发频繁FULL GC)-XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=512M