25_模拟频繁 Full GC 案例
约 2770 字大约 9 分钟
2024-08-10
JVM 参数配置
下面这些参数基于 JDK1.8 版本来配置的,忘记怎么设置的可以回看 07_动手实验:系统部署时如何设置JVM内存大小 的内容
//初始新生代大小 最大新生代大小 初始堆内存大小 最大堆内存大小 Eden区占比新生代比例 大对象阈值 新生代垃圾器 ParNew 老年代垃圾器 CMS 打印详细 GC 日志 打印每次发生GC的时间 将GC 日志写入磁盘文件
-XX:NewSize=100M -XX:MaxNewSize=100M -XX:InitialHeapSize=200M -XX:MaxHeapSize=200M -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=20M -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.log我们这里设置堆内存 200MB、新生代 100MB、老年代 100MB,大对象阈值为 20MB
代码
下面模拟的 Java 代码中,我们先停顿 20 秒,方便我们找到对应进程 PID,然后执行 jstat 命令
每秒钟执行一次 loadData() 方法,分配 4 个 10MB 的数组,完了就成为垃圾对象
data1、data2 两个 10MB 数组想是被变量引用的存活对象,此时 Eden 区已被占用六七十 MB 空间了
接着,data3 变量依次指向两个 10MB 数组,这是为了触发 Young GC
/**
* @ClassName AnalysisEngineDemo
* @Desciption
* @Author MaRui
* @Date 2024/2/2 18:26
* @Version 1.0
*/
public class AnalysisEngineDemo {
public static void main(String[] args) throws InterruptedException {
TimeUnit.SECONDS.sleep(20);
while (true) {
loadData();
}
}
private static void loadData() throws InterruptedException {
byte[] data = null;
for (int i = 0; i < 4; i++) {
data = new byte[10 * 1024 * 1024];
}
data = null;
byte[] data1 = new byte[10 * 1024 * 1024];
byte[] data2 = new byte[10 * 1024 * 1024];
byte[] data3 = new byte[10 * 1024 * 1024];
data3 = new byte[10 * 1024 * 1024];
TimeUnit.SECONDS.sleep(1);
}
}结果分析
程序运行起来之后,我们可以看到,在第一秒就发生了一次 Young GC,下面我们对日志一行一行进行分析
root@DESKTOP-GQ2O8E7 MINGW64 ~
$ jps
21696 AnalysisEngineDemo
7552 RemoteMavenServer36
11812
26836 RemoteMavenServer
11112
31516 Jps
33100 Launcher
root@DESKTOP-GQ2O8E7 MINGW64 ~
$ jstat -gc 21696 1000 1000
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
10240.0 10240.0 0.0 1479.0 81920.0 10240.0 102400.0 30722.1 4864.0 3832.9 512.0 424.2 1 0.014 0 0.000 0.014
10240.0 10240.0 1808.0 0.0 81920.0 23630.1 102400.0 51204.2 4864.0 3833.9 512.0 424.2 2 0.022 1 0.000 0.023
10240.0 10240.0 0.0 1866.6 81920.0 32251.9 102400.0 61444.2 4864.0 3834.0 512.0 424.2 3 0.027 1 0.000 0.027
10240.0 10240.0 1594.4 0.0 81920.0 42517.2 102400.0 30724.1 4864.0 3834.0 512.0 424.2 4 0.028 2 0.002 0.029
10240.0 10240.0 0.0 1908.6 81920.0 52773.5 102400.0 40964.2 4864.0 3834.0 512.0 424.2 5 0.029 2 0.002 0.031
10240.0 10240.0 1860.3 0.0 81920.0 63023.9 102400.0 51204.2 4864.0 3834.0 512.0 424.2 6 0.031 2 0.002 0.033
10240.0 10240.0 0.0 1991.3 81920.0 73270.6 102400.0 61444.2 4864.0 3834.0 512.0 424.2 7 0.033 3 0.002 0.035
10240.0 10240.0 0.0 1923.2 81920.0 10240.0 102400.0 40964.2 4864.0 3834.0 512.0 424.2 9 0.046 4 0.004 0.050
10240.0 10240.0 2008.8 0.0 81920.0 22079.4 102400.0 61444.2 4864.0 3834.0 512.0 424.2 10 0.049 5 0.004 0.053
10240.0 10240.0 0.0 1912.1 81920.0 32320.6 102400.0 71684.2 4864.0 3834.0 512.0 424.2 11 0.051 5 0.004 0.055
10240.0 10240.0 1805.1 0.0 81920.0 42561.3 102400.0 30724.1 4864.0 3834.0 512.0 424.2 12 0.052 6 0.005 0.057
10240.0 10240.0 0.0 1911.4 81920.0 52801.8 102400.0 40964.2 4864.0 3834.0 512.0 424.2 13 0.053 6 0.005 0.059
10240.0 10240.0 1860.9 0.0 81920.0 63042.1 102400.0 51204.2 4864.0 3834.0 512.0 424.2 14 0.055 6 0.005 0.060
10240.0 10240.0 0.0 1792.3 81920.0 73282.3 102400.0 61444.2 4864.0 3834.0 512.0 424.2 15 0.057 7 0.005 0.062
10240.0 10240.0 0.0 0.0 81920.0 10240.0 102400.0 42315.8 4864.0 3834.0 512.0 424.2 17 0.064 8 0.006 0.070
10240.0 10240.0 0.0 0.0 81920.0 22082.5 102400.0 62795.8 4864.0 3834.0 512.0 424.2 18 0.066 9 0.007 0.073
10240.0 10240.0 0.0 0.0 81920.0 32322.6 102400.0 73035.8 4864.0 3834.0 512.0 424.2 19 0.068 9 0.007 0.075
10240.0 10240.0 0.0 0.0 81920.0 42562.6 102400.0 32074.7 4864.0 3834.0 512.0 424.2 20 0.068 10 0.008 0.077
10240.0 10240.0 0.0 0.0 81920.0 52802.6 102400.0 42314.8 4864.0 3834.0 512.0 424.2 21 0.070 10 0.008 0.078
10240.0 10240.0 0.0 0.0 81920.0 63042.6 102400.0 52554.8 4864.0 3834.0 512.0 424.2 22 0.071 10 0.008 0.080
10240.0 10240.0 0.0 0.0 81920.0 73282.7 102400.0 62794.8 4864.0 3834.0 512.0 424.2 23 0.073 11 0.008 0.081
10240.0 10240.0 0.0 0.0 81920.0 10240.0 102400.0 42314.8 4864.0 3834.0 512.0 424.2 25 0.078 12 0.009 0.087
10240.0 10240.0 0.0 0.0 81920.0 22082.6 102400.0 62794.8 4864.0 3834.0 512.0 424.2 26 0.080 13 0.010 0.090
10240.0 10240.0 0.0 0.0 81920.0 32322.6 102400.0 73034.8 4864.0 3834.0 512.0 424.2 27 0.082 13 0.010 0.092
10240.0 10240.0 0.0 0.0 81920.0 42562.6 102400.0 32074.7 4864.0 3834.0 512.0 424.2 28 0.083 14 0.011 0.094
10240.0 10240.0 0.0 0.0 81920.0 52802.6 102400.0 42314.8 4864.0 3834.0 512.0 424.2 29 0.084 14 0.011 0.095第一次执行 loadData() 方法时,Eden 区占用 0MB,老年代占用 0MB
即将执行 data3 = new byte[10 * 1024 * 1024]; 这行代码时,Eden 区已经被占用了 70MB 内存
Eden 区内存大小为 80MB,显然无法继续放入一个 10MB 数组对象,这就是为什么我们程序运行起来第一秒就发生 Young GC
Survivor 内存大小为 10MB,无法放入 Eden 区存活对象,所以 Eden 区 data1、data2、data3 变量引用的共 30MB 数组对象直接放入老年代,Eden 区回收掉 data 现在没引用的 40MB 数组对象,Eden 区放入 data3 = new byte[10 * 1024 * 1024]; 创建的 10MB 数组
下面日志证明了我们的猜想,Young GC 之后 Eden 区占用 10MB,老年代占用 30MB
EC EU OC OU YGC YGCT FGC FGCT GCT
81920.0 10240.0 102400.0 30722.1 1 0.014 0 0.000 0.014第二次执行 loadData() 方法时,Eden 区占用 10MB,老年代占用 30MB
即将执行 byte[] data3 = new byte[10 * 1024 * 1024]; 时,Eden 区内存占用为 70MB,无法放入一个 10MB 数组对象,发生 Young GC
这时,data1、data2 变量引用的共 20MB 数组对象直接进入老年代,Eden 区回收掉 data 现在没引用的 40MB 数组对象,放入后续 data3 引用的共计 20MB 数组对象
下面日志也验证了我们的猜想,Young GC 之后 Eden 区占用 20MB,老年代占用 50MB
EC EU OC OU YGC YGCT FGC FGCT GCT
81920.0 23630.1 102400.0 51204.2 2 0.022 1 0.000 0.023第三次执行 loadData() 方法时,Eden 区占用 20MB,老年代占用 30MB
即将执行 byte[] data2 = new byte[10 * 1024 * 1024]; 时,Eden 区内存占用为 70MB,无法放入一个 10MB 数组对象,发生 Young GC
这时,data1 变量引用的共 10MB 数组对象直接进入老年代,Eden 区回收掉 data 现在没引用的 40MB 数组对象,放入后续 data2、data3 引用的共计 30MB 数组对象
下面日志也验证了我们的猜想,Young GC 之后 Eden 区占用 30MB,老年代占用 60MB
EC EU OC OU YGC YGCT FGC FGCT GCT
81920.0 32251.9 102400.0 61444.2 3 0.027 1 0.000 0.027第四次执行 loadData() 方法时,Eden 区占用 30MB,老年代占用 60MB
即将执行 byte[] data1 = new byte[10 * 1024 * 1024]; 时,Eden 区内存占用为 70MB,无法放入一个 10MB 数组对象,发生 Young GC
这时,data 变量引用的共 10MB 数组对象直接进入老年代,Eden 区回收掉 data 现在没引用的 30MB 数组对象,放入后续 data1、data2、data3 引用的共计 40MB 数组对象
下面日志也验证了我们的猜想,Full GC 之后 Eden 区占用 40MB
但是老年代占用 30MB,是不符合我们预期的 70MB,老年代剩余 40MB,历次 Young GC 进入老年代对象平均大小为 20MB,老年代空间担保机制是够的,不会触发 Full GC
这个触发 Full GC 的搞不懂,只能猜测他 Full GC 后老年代只回收了 40MB 内存,还有需要注意的是,日志每一行不一定代表执行一次 loadData() 方法
EC EU OC OU YGC YGCT FGC FGCT GCT
81920.0 42517.2 102400.0 30724.1 4 0.028 2 0.002 0.029看日志中的
GC时间,29次Young GC耗时84毫秒,平均一次接近3毫秒,14次Full GC耗时11毫秒,平均一次一毫秒不到,为什么Young GC这么耗时呢?
按照上述代码,每次Full GC都是由Young GC触发的,得等Full GC执行完毕后,Young GC才能把存活对象放入老年代,这才算结束,这就导致Young GC显示的耗时比较长
性能优化
之前 JVM 参数配置下,最大的问题是每次 Young GC 过后存活对象太多,频繁进入老年代,触发 Full GC,我们调大年轻代以及增加 Survivor 内存空间即可
//初始新生代大小 最大新生代大小 初始堆内存大小 最大堆内存大小 Eden区占比新生代比例 大对象阈值 新生代垃圾器 ParNew 老年代垃圾器 CMS 打印详细 GC 日志 打印每次发生GC的时间 将GC 日志写入磁盘文件
-XX:NewSize=200M -XX:MaxNewSize=200M -XX:InitialHeapSize=300M -XX:MaxHeapSize=300M -XX:SurvivorRatio=2 -XX:PretenureSizeThreshold=20M -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.log我们把堆大小调整为 300MB,年轻代调整为 200MB,同时 -XX:SurvivorRatio=2 表示,Eden : Survivor : Survivor = 2 : 1 : 1,也就是 Eden 区 100MB,每个 Survivor 区 50MB
我们运行程序,用 jstat 监控运行状态如下,每次 Young GC 过后会有 10MB 左右存活对象进入 Survivor,几乎没有对象进入老年代
每次 Young GC 耗时不过两三毫秒
$ jstat -gc 15180 1000 100
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
51200.0 51200.0 0.0 0.0 102400.0 92160.6 102400.0 0.0 4480.0 769.8 384.0 75.8 0 0.000 0 0.000 0.000
51200.0 51200.0 0.0 1546.8 102400.0 83877.1 102400.0 0.0 4864.0 3832.6 512.0 424.2 1 0.001 0 0.000 0.001
51200.0 51200.0 12041.5 0.0 102400.0 75630.1 102400.0 0.0 4864.0 3833.6 512.0 424.2 2 0.005 0 0.000 0.005
51200.0 51200.0 0.0 12250.4 102400.0 63363.7 102400.0 0.0 4864.0 3833.7 512.0 424.2 3 0.010 0 0.000 0.010
51200.0 51200.0 12096.6 0.0 102400.0 53152.6 102400.0 0.0 4864.0 3833.7 512.0 424.2 4 0.011 0 0.000 0.011
51200.0 51200.0 0.0 1868.5 102400.0 42931.1 102400.0 0.0 4864.0 3833.7 512.0 424.2 5 0.012 0 0.000 0.012
51200.0 51200.0 12319.4 0.0 102400.0 32703.0 102400.0 0.0 4864.0 3833.7 512.0 424.2 6 0.014 0 0.000 0.014
51200.0 51200.0 0.0 22560.3 102400.0 22470.7 102400.0 0.0 4864.0 3833.7 512.0 424.2 7 0.020 0 0.000 0.020
51200.0 51200.0 32592.5 0.0 102400.0 10240.0 102400.0 0.0 4864.0 3833.7 512.0 424.2 8 0.032 0 0.000 0.032
51200.0 51200.0 32592.5 0.0 102400.0 94155.7 102400.0 0.0 4864.0 3833.7 512.0 424.2 8 0.032 0 0.000 0.032
51200.0 51200.0 0.0 0.0 102400.0 83918.8 102400.0 1353.9 4864.0 3833.7 512.0 424.2 9 0.037 0 0.000 0.037
51200.0 51200.0 10240.0 0.0 102400.0 73680.9 102400.0 1353.9 4864.0 3833.7 512.0 424.2 10 0.039 0 0.000 0.039
51200.0 51200.0 0.0 10240.0 102400.0 63442.1 102400.0 1353.9 4864.0 3833.7 512.0 424.2 11 0.040 0 0.000 0.040
51200.0 51200.0 10240.0 0.0 102400.0 53203.0 102400.0 1353.9 4864.0 3833.7 512.0 424.2 12 0.042 0 0.000 0.042
51200.0 51200.0 0.0 0.0 102400.0 42963.5 102400.0 1353.9 4864.0 3833.7 512.0 424.2 13 0.042 0 0.000 0.042
51200.0 51200.0 10240.0 0.0 102400.0 32723.8 102400.0 1353.9 4864.0 3833.7 512.0 424.2 14 0.044 0 0.000 0.044
51200.0 51200.0 0.0 20480.0 102400.0 22484.0 102400.0 1353.9 4864.0 3833.7 512.0 424.2 15 0.046 0 0.000 0.046
51200.0 51200.0 30720.0 0.0 102400.0 10240.0 102400.0 1353.9 4864.0 3833.7 512.0 424.2 16 0.050 0 0.000 0.050
51200.0 51200.0 30720.0 0.0 102400.0 94164.3 102400.0 1353.9 4864.0 3833.7 512.0 424.2 16 0.050 0 0.000 0.050
51200.0 51200.0 0.0 0.0 102400.0 83924.4 102400.0 1353.9 4864.0 3833.7 512.0 424.2 17 0.051 0 0.000 0.051
51200.0 51200.0 10240.0 0.0 102400.0 73684.4 102400.0 1353.9 4864.0 3833.7 512.0 424.2 18 0.052 0 0.000 0.052
51200.0 51200.0 0.0 10240.0 102400.0 63444.4 102400.0 1353.9 4864.0 3833.7 512.0 424.2 19 0.054 0 0.000 0.054
51200.0 51200.0 10240.0 0.0 102400.0 53204.4 102400.0 1353.9 4864.0 3833.7 512.0 424.2 20 0.056 0 0.000 0.056
51200.0 51200.0 0.0 0.0 102400.0 42964.4 102400.0 1353.9 4864.0 3833.7 512.0 424.2 21 0.056 0 0.000 0.056
51200.0 51200.0 10240.0 0.0 102400.0 32724.4 102400.0 1353.9 4864.0 3833.7 512.0 424.2 22 0.057 0 0.000 0.057
51200.0 51200.0 0.0 20480.0 102400.0 22484.4 102400.0 1353.9 4864.0 3833.7 512.0 424.2 23 0.060 0 0.000 0.060
51200.0 51200.0 30720.0 0.0 102400.0 10240.0 102400.0 1353.9 4864.0 3833.7 512.0 424.2 24 0.064 0 0.000 0.064
51200.0 51200.0 30720.0 0.0 102400.0 94164.5 102400.0 1353.9 4864.0 3833.7 512.0 424.2 24 0.064 0 0.000 0.064
51200.0 51200.0 0.0 0.0 102400.0 83924.5 102400.0 1353.9 4864.0 3833.7 512.0 424.2 25 0.064 0 0.000 0.064
51200.0 51200.0 10240.0 0.0 102400.0 73684.5 102400.0 1353.9 4864.0 3833.7 512.0 424.2 26 0.066 0 0.000 0.066
51200.0 51200.0 0.0 10240.0 102400.0 63444.5 102400.0 1353.9 4864.0 3833.7 512.0 424.2 27 0.067 0 0.000 0.067
51200.0 51200.0 10240.0 0.0 102400.0 53204.5 102400.0 1353.9 4864.0 3833.7 512.0 424.2 28 0.069 0 0.000 0.069
51200.0 51200.0 0.0 0.0 102400.0 42964.5 102400.0 1353.9 4864.0 3833.7 512.0 424.2 29 0.069 0 0.000 0.069
51200.0 51200.0 10240.0 0.0 102400.0 32724.4 102400.0 1353.9 4864.0 3833.7 512.0 424.2 30 0.071 0 0.000 0.071
51200.0 51200.0 0.0 20480.0 102400.0 22484.4 102400.0 1353.9 4864.0 3833.7 512.0 424.2 31 0.074 0 0.000 0.074
51200.0 51200.0 30720.0 0.0 102400.0 10240.0 102400.0 1353.9 4864.0 3833.7 512.0 424.2 32 0.078 0 0.000 0.078