使用一段代码模拟出频繁 Young GC 的一个场景:

(1) 模拟代码的 JVM 参数

  1. -XX:NewSize=104857600 -XX:MaxNewSize=104857600 -XX:InitialHeapSize=209715200 -XX:MaxHeapSize=209715200 -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15 -XX:PretenureSizeThreshold=3145728 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.log
  • 堆内存 200MB;
  • 年轻代 100MB,Eden 80MB,每块 Suvivor 10MB;
  • 老年代 100MB;

(2) 示例程序

public class Demo1 {
    public static void main(String[] args) throws InterruptedException {
        Thread.sleep(30000);
        while (true){
            loadData();
        }
    }

    private static void loadData() throws InterruptedException {
        byte[] data = null;
        for(int i=0; i<50; i++){
            data = new byte[100*1024];
        }
        data = null;
        Thread.sleep(1000);
    }
}
  • “Thread.sleep(30000);”,刚开始先休眠30s,获取 Java 程序的 PID;
  • loadData() 会模拟每秒50个请求,每个请求创建一个100KB的数组,每个数组的生命周期都很短,创建之后就变成垃圾对象;
  • “while(true)”,模拟系统按照每秒50个请求不停的运行;

(3) 通过 jstat 观察程序的运行状态

  • 获取程序 PID;

    $ jps
    14896 Demo1
    18632 Jps
    
  • 每隔一秒打印一次统计信息,连续打印1000次;

    • EU:(新生代对象增长速率、YoungGC的频率)
      • 从开始8M…11M…16M…21M,Eden 每秒都会新增 5M 左右的对象;
      • 当到达 80M 左右的时候,再要分配 5M 的对象失败了,触发一次 Young GC,Eden 降低到 4M 的使用空间;
      • 对象增速大致为每秒 5M 左右,大致在十几秒左右会触发一次 Young GC;
    • YGC,YGCT:(YoungGC 耗时)
      • 一次回收 70多MB 对象,大概 1ms;
    • S1U:(每次 YoungGC 后有多少对象存活下来)
      • S1U 是 Survivor 中被使用的内存,之前一直0,一次 YoungGC 后变成1.2MB,这就是存活下来的对象大小;
    • OU:(每次 YoungGC 后有多少对象进入老年代)
      • OU 是老年代被使用的内存量,YoungGC 前后都是0,说明这个系统运行良好;
    • FGC,FGCT:(Full GC 频率、耗时)

    image.png