使用一段代码模拟出频繁 Young GC 的一个场景:
(1) 模拟代码的 JVM 参数
-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 频率、耗时)
- EU:(新生代对象增长速率、YoungGC的频率)