系统频繁Full GC导致系统卡顿是怎么回事?以下为业务场景出现的问题:
机器配置:2核4GJVM内存大小:2G系统运行时间:7天期间发生的Full GC次数和耗时:500多次,200多秒期间发生的Young GC次数和耗时:1万多次,500多秒
‐Xms1536M ‐Xmx1536M ‐Xmn512M ‐Xss256K ‐XX:SurvivorRatio=6 ‐XX:MetaspaceSize=256M ‐XX:MaxMetaspaceSize=256M ‐XX:+UseParNewGC ‐XX:+UseConcMarkSweepGC ‐XX:CMSInitiatingOccupancyFraction=75 ‐XX:+UseCMSInitiatingOccupancyOnly

对于对象动态年龄判断机制导致的full gc较为频繁可以先试着优化下JVM参数,把年轻代适当调大点:
‐Xms1536M ‐Xmx1536M ‐Xmn1024M ‐Xss256K ‐XX:SurvivorRatio=6 ‐XX:MetaspaceSize=256M ‐XX:MaxMetaspaceSize=256M ‐XX:+UseParNewGC ‐XX:+UseConcMarkSweepGC ‐XX:CMSInitiatingOccupancyFraction=92 ‐XX:+UseCMSInitiatingOccupancyOnly
设置程序启动参数后,JVM内存模型如下图所示:
优化完发现没什么变化,full gc的次数比minor gc的次数还多了:

1 import java.util.ArrayList;23 @RestController4 public class IndexController {56 @RequestMapping("/user/process")7 public String processUserData() throws InterruptedException {8 ArrayList<User> users = queryUsers();910 for (User user: users) {11 //TODO 业务处理12 System.out.println("user:" + user.toString());13 }14 return "end";15 }1617 /**18 * 模拟批量查询用户场景19 * @return20 */21 private ArrayList<User> queryUsers() {22 ArrayList<User> users = new ArrayList<>();23 for (int i = 0; i < 5000; i++) {24 users.add(new User(i,"zhuge"));25 }26 return users;27 }28 }
可以看到,代码也需要优化,一次性查询出5千个对象,大约1个User对象是1KB,那么5千个就是500M,也就是说一次性需要查询出500M的新生对象,那么根据老年代担保机制,年轻代每minor gc之前JVM都会计算下老年代剩余可用空间 ,如果这个可用空间小于年轻代里现有的所有对象大小之和(包括垃圾对象),那么就会触发一次fullGC,显然我们的老年代根本无法存储新生的对象,需要触发一次FullGC,然后再执行一次MinoGC,执行完之后,如果存活的对象还是大于老年代剩余空间,就又会导致一次FullGC,相当于一次MinorGC,会导致两次FullGC,依次类推,系统运行下去,FullGC的频率会不断增大,导致我们的系统运行会越来越卡顿。
综上所述,我们需要尽量减少这种朝生夕死对象导致的FullGC。
总结一下自己的JVM调优思路,仅供参考:
1)尽量减少YongGC后存活的对象大于Survivor区50%,让存活对象尽量保留在年轻代,尽量别让对象进入老年代。
2)尽量减少FullGC的频率,避免频繁FullGC对JVM性能的影响。