乐趣区

关于jvm调优:JVM频繁GC分析

本文记录一次频繁 GC 的剖析

问题

查看我的项目日志发现 GC 频繁,简直几秒钟一次

查看 GC 日志

[GC (Allocation Failure) [PSYoungGen: 6816K->320K(8192K)] 82693K->76229K(187904K), 0.0032930 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 6464K->224K(8192K)] 82373K->76165K(187904K), 0.0067897 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 6368K->192K(8192K)] 82309K->76149K(187904K), 0.0052315 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 6336K->1152K(8192K)] 82293K->77117K(187904K), 0.0026567 secs] [Times: user=0.05 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 7296K->1184K(8192K)] 83261K->77157K(187904K), 0.0049316 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 7202K->1056K(8192K)] 83175K->77037K(187904K), 0.0053079 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 7181K->912K(8192K)] 83162K->76901K(187904K), 0.0045949 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 7056K->544K(8192K)] 83045K->76557K(187904K), 0.0066989 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 

GC 日志打印 YGC 日志十分频繁,几秒钟一次。
PSYoungGen 是应用 Parallel Scavenage 垃圾收集器的年老代
从日志上能够看到 7056K->544K(8192K) 年老代总大小十分小只有 8M,这就是产生频繁 YGC 的起因,当 new 了一个新对象,Young 区内存不够调配时,就会触发一次 YGC。
那么为什么 Young 区会这么小呢?

Jstat

通过 jstat -gc 2668 10000 7 查看 GC 状况

$   jstat -gc 2668 10000 7
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT
2048.0 1024.0  0.0   704.0   5632.0   5504.2   179712.0   80893.3   55552.0 53214.6 6912.0 6470.5   9573   69.553   6      0.593   70.146
2048.0 2048.0  0.0   1184.1  5120.0   911.5    179712.0   80973.3   55552.0 53214.6 6912.0 6470.5   9577   69.573   6      0.593   70.167
2048.0 2048.0 1024.0  0.0    5120.0   1978.0   179712.0   81005.3   55552.0 53214.6 6912.0 6470.5   9580   69.596   6      0.593   70.189
2048.0 2048.0  0.0   224.0   5120.0   3156.5   179712.0   81069.3   55552.0 53214.6 6912.0 6470.5   9583   69.614   6      0.593   70.207
2048.0 2048.0 1216.1  0.0    5120.0   4161.0   179712.0   81125.3   55552.0 53214.6 6912.0 6470.5   9586   69.632   6      0.593   70.225
2048.0 2560.0 1984.0  0.0    4608.0   2132.9   179712.0   81213.3   55552.0 53214.6 6912.0 6470.5   9590   69.660   6      0.593   70.253
3584.0 3584.0 2144.0  0.0    4096.0   2305.9   179712.0   81381.3   55552.0 53214.6 6912.0 6470.5   9594   69.683   6      0.593   70.276

EC eden 区大小
EU eden 区已应用空间大小
S0C 示意 Survival 0 区容量
S1C 示意 Survival 1 区容量
S0U 示意 Survival 0 区已应用空间大小
S1U 示意 Survival 1 区已应用空间大小
相干参数看考文档(java8):https://docs.oracle.com/javase/8/docs/technotes/tools/windows/jstat.html#BEHHGFAE

从下面能够看到,eden 区、S0 区和 S1 区大小始终在变动而且并不一定相等,那么为什么堆区大小始终在变动呢?

查了一下发现 jvm 有个参数 UseAdaptiveSizePolicy 开启了,该参数是默认开启的,查看 jvm 参数:

$ java -XX:+PrintFlagsFinal -version | grep UseAdaptiveSizePolicy
     bool UseAdaptiveSizePolicy                     = true                                {product}

那么能够确定起因基本上是 jvm 因为开启了 UseAdaptiveSizePolicy 参数,jvm 主动调整堆大小,因为 Young 区被调整太小导致频繁产生 YGC,咱们能够通过调整启动参数来防止这种状况。

从新设置启动参数

-XX:+PrintGCDetails -XX:-UseAdaptiveSizePolicy -XX:SurvivorRatio=8 -Xmn100M 

-XX:+PrintGCDetails 启用日志
-XX:-UseAdaptiveSizePolicy 敞开主动跳转堆区大小
-XX:SurvivorRatio=8 设置 eden 区和 survivor 区大小比 8
-Xmn100M 置 Young 区大小

退出移动版