案例1(xmx与xms不一致导致的问题)
某天项目机器进行迁移后频繁发生FullGC报警,查询内存信息和启动参数
启动参数:-XX:MetaspaceSize=128M; -Xmx4096M -Xms512M -Xss256k -XX:+UseConcMarkSweepGC -XX:+UseParNewGC
内存信息如图1
(图1:JVM总内存)
FullGC次数监控如图2
(图2:FullGC次数)
查看内存和GC现象,可以发现以下问题
最大堆内存配置为 -Xmx4096M,但是图1所示,总内存还未达到1000M已经发生FullGC,JVM配置的最大内存一直未生效。
原因是-Xmx4096M -Xms512M中配置的最小堆内存和最大堆内存不一致,当JVM的当前堆内存已经堆满之后会先触发FullGC,如果FullGC之后内存依然不足,则会触发堆自动扩容,然而该应用每次FullGC之后都有大量内存被回收,JVM的空间暂时能够满足应用使用,因此不会进行扩容。对该现象进行复现,将垃圾回收器修改为JAVA8 默认的垃圾回收器组合(parallel scavenge + parallel old)时,不会进行FullGC,而是直接完成扩容,而使用CMS+ParNew的组合则会发生FullGC。因此判断先进行FullGC 再进行扩容,应该与垃圾回收器有关。当然本次调优的重点还是将最小堆内存和最大堆内存保持一致。寄防止内存不足发生FullGC,也防止向系统申请内存浪费时间。
-Xmx4096M -Xms4096M
案例2(年轻代不足)
某天线上突然有个服务发生了短暂的超时,查看监控,发现机器发生了FullGC导致STW。 查看内存监控,发现该机器老年代内存量一直缓慢增长,增长到了上限之后,就发生FullGC(图已经丢了)。查看JVM的启动参数的关键配置如下:
-Xms3600m -Xmx3600m -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=512m
因为面向用户的服务还是期望STW的时间短暂或者永远不要出现FullGC,因此调整JVM参数
-Xms4g -Xmx4g -Xmn2g -XX:SurvivorRatio=4
堆内存改为4G保持和其他项目统一,默认情况下年轻代/老年代比例为1:2,堆为4G时,默认年轻代大小为4/3G ,将年轻代调整为2G,增加年轻代的大小,是防止当QPS较高的时候,年轻带可能存在来不及回收的情况,导致新生成的对象时内存不足,导致不足龄的年轻代对象直接进入老年代。默认情况下eden区和survivor区的比例为8:1:1,调整比例为4:1:1,可以理解为增大S0、S1,减小Eden区。根据之前的判断,年轻带无法快速清空,导致部分对象晋升到老年代,在这个推断的前提下,Eden区过大,并且在youngGC时,eden中存活下来的对象超过survior区的大小,会导致对象未达到晋升年龄就进去老年代。
调整启动参数后观察,发现老年代内存稳定,不再持续变大。