JVM频繁Full GC导致服务不可用定位过程

JVM频繁Full GC导致服务不可用定位过程

    • 背景:
    • 问题描述:
    • JVM启动参数配置:
    • Jstat 实时监控
    • 真相逐渐浮现
    • jstat 分析gc原因:
    • 使用mat辅助分析
    • 定位哪里引用了groovy导致内存泄漏
    • 查看ShardingJDBC发版记录
    • 写在最后

背景:

公司推行微服务策略,我负责的XX模块相对于其他业务来讲相对独立,所以作为微服务推行的试点。于是

  • 分析业务边界
  • 做相关的架构升级
    • 从Spring3.X升级到Spring5.X(引入了SpringBoot2.0)
    • 从JDK7升级到JDK8(老年代被MetaSpace取代)
    • ShardingJDBC从1.4版本升级到3.00.M1版本

问题描述:

重构系统上线后,发现运行一段时间后,系统会出现频繁的Full GC,频率几乎1秒一次,导致服务长时间卡死(10+mins到30+mins不等),但是卡死一段时间后会恢复,再运行一段时间,服务会被Linux内核kill了。

JVM启动参数配置:

java -Dspring.profiles.active=prod -server -Xms4g -Xmx4g -Xmn2g -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m -XX:+UseParallelGC -XX:+UseParallelOldGC -verbose:gc -Xloggc:/mnt/gc.log -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintHeapAtGC -XX:+PrintGCDateStamps -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/mnt/fc-income.dump -XX:-OmitStackTraceInFastThrow -jar /mnt/app/income.jar &

Jstat 实时监控

JVM频繁Full GC导致服务不可用定位过程_第1张图片
从上图可以看出Eden和Old都满了,导致几乎2秒一次的full gc,此时,服务大部分时间耗在full gc,而full gc是stop the world。

真相逐渐浮现

既然是堆内存不够,那么就加大堆内存

java -Dspring.profiles.active=prod -server -Xms8g -Xmx8g -Xmn3g -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m -XX:+UseParallelGC -XX:+UseParallelOldGC -verbose:gc -Xloggc:/mnt/gc.log -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintHeapAtGC -XX:+PrintGCDateStamps -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/mnt/fc-income.dump -XX:-OmitStackTraceInFastThrow -jar /mnt/app/income.jar &

即使加大了堆内存,服务还是运行一段时间后频繁full gc
JVM频繁Full GC导致服务不可用定位过程_第2张图片
这时候发现,年轻代和老年代的使用率都很低,连MetaSpace的使用率也只有67.34%,但是几乎是2秒就一次full gc
此时就百思不得其解了,为什么明明堆内存没满,可是就是full gc呢??

jstat 分析gc原因:

[root]# jstat -options
-class           -- 显示加载class的数量,及所占空间等信息
-compiler        -- 显示VM实时编译的数量等信息
-gc              -- 可以显示gc的信息,查看gc的次数,及时间
-gccapacity      -- VM内存中三代(young,old,perm)对象的使用和占用大小
-gccause         -- 统计gc信息,并且打印gc的原因
-gcmetacapacity  -- metaSpace的信息及其占用量
-gcnew           -- 年轻代对象的信息
-gcnewcapacity   -- 年轻代对象的信息及其占用量
-gcold           -- old代对象的信息
-gcoldcapacity   -- old代对象的信息及其占用量
-gcutil          -- 统计gc信息(显示使用的百分比)
-printcompilation-- 当前VM执行的信息

这时候,使用jstat -gccause pid 来分析引起gc的原因
JVM频繁Full GC导致服务不可用定位过程_第3张图片
这时候就很明显了,是由于metaSpace空间满了导致的。一般来讲,metaSpace存放的是类的元数据。

使用mat辅助分析

JVM频繁Full GC导致服务不可用定位过程_第4张图片
1258869328/1024/1024/1024 ~=1.2G
请参考文章
groovy导致的内存泄漏

定位哪里引用了groovy导致内存泄漏

利用:mvn dependency:tree查看项目依赖关系
JVM频繁Full GC导致服务不可用定位过程_第5张图片
很明显,就是之前重构的时候升级ShardingJDBC引入的坑。

查看ShardingJDBC发版记录

JVM频繁Full GC导致服务不可用定位过程_第6张图片
刚好在3.0.0M2里面解决了这个问题(原来使用的是M1版本)

写在最后

生产环境千万别弄不稳定的版本,里程碑版本其实是不稳定的。。

你可能感兴趣的:(JVM)