记录一次生产jvm问题的排查

记录一次生产问题的排查

第一天晚上

现象

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

相关推荐
书院门前细致的苹果10 小时前
JVM 全面详解:深入理解 Java 的核心运行机制
java·jvm
稻草人想看远方11 小时前
GC垃圾回收
java·开发语言·jvm
我真的是大笨蛋13 小时前
从源码和设计模式深挖AQS(AbstractQueuedSynchronizer)
java·jvm·设计模式
我真的是大笨蛋16 小时前
G1 垃圾收集器深入解析
java·jvm·笔记·缓存
好多1718 小时前
《JVM如何排查OOM》
开发语言·jvm·python
getdu21 小时前
JVM第一部分
jvm
海梨花1 天前
字节一面 面经(补充版)
jvm·redis·后端·面试·juc
Mr_Xuhhh1 天前
项目-sqlite类的实现
java·jvm·sqlite
佛祖让我来巡山1 天前
深入理解Java对象:从创建到内存访问的JVM底层机制
jvm·对象创建过程·对象是如何创建的
用手手打人1 天前
JVM详解(一)--JVM和Java体系结构
jvm