まじめにJVMチューニング: 第4回 GCアルゴリズムを変えてみる

2014-02-10

Java SE 7 Update 4から、GCの新しいアルゴリズムとして「G1GC」が追加されました。
Java6でもupdate 14から実験的に導入されているようです。

GCアルゴリズムについては下記に概要説明がありました。

G1GCアルゴリズムの詳細についてはこちらに超詳しく記載されています。

GCアルゴリズムの技術的な詳細解説だったら下記のページがよかったです。

G1GCを使うためには

-XX:+UseConcMarkSweepGC

をはずし、

-XX:+UseG1GC

を追加します。

G1GCはヒープを分割して管理しますが、分割のデフォルトサイズが1MBと小さいのでもう少し大きくします。

-XX:G1HeapRegionSize=8M

MaxGCPauseMillisでGC時に停止する最大ミリ秒を指定できます。

-XX:MaxGCPauseMillis=200

パラメータ例:

-XX:PermSize=128M -XX:MaxPermSize=128M
-Xms3584M -Xmx3584M
-Xloggc:/var/log/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps
-XX:+UseG1GC
-XX:MaxTenuringThreshold=32
-XX:NewRatio=1 -XX:SurvivorRatio=2 -XX:TargetSurvivorRatio=90
-XX:MaxGCPauseMillis=200
-XX:G1HeapRegionSize=8M

これで実行し、 GCログを見てみます。
CMSアルゴリズムを指定したときは

2013-12-26T19:53:13.464+0900: 180.660: [GC 180.660: [ParNew: 979319K->90488K(1376256K), 0.0524510 secs] 980840K->92008K(3211264K), 0.0526950 secs] [Times: user=0.09 sys=0.00, real=0.05 secs]
2013-12-26T19:53:21.344+0900: 188.541: [GC 188.541: [ParNew: 1007992K->61900K(1376256K), 0.0498060 secs] 1009512K->63421K(3211264K), 0.0500570 secs] [Times: user=0.09 sys=0.01, real=0.05 secs]

のようなログでしたが、G1GCでは

 [Times: user=0.01 sys=0.00, real=0.01 secs]
2013-12-26T22:48:30.325+0900: 0.392: [GC pause (young), 0.00362900 secs]
   [Parallel Time:   3.6 ms]
      [GC Worker Start Time (ms):  392.1  395.7]
      [Update RS (ms):  0.0  0.0
       Avg:   0.0, Min:   0.0, Max:   0.0]
         [Processed Buffers : 2 0
          Sum: 2, Avg: 1, Min: 0, Max: 2]
      [Ext Root Scanning (ms):  0.8  0.0
       Avg:   0.4, Min:   0.0, Max:   0.8]
      [Mark Stack Scanning (ms):  0.0  0.0
       Avg:   0.0, Min:   0.0, Max:   0.0]
      [Scan RS (ms):  0.0  0.0
       Avg:   0.0, Min:   0.0, Max:   0.0]
      [Object Copy (ms):  2.7  0.0
       Avg:   1.3, Min:   0.0, Max:   2.7]
      [Termination (ms):  0.1  0.0
       Avg:   0.1, Min:   0.0, Max:   0.1]
         [Termination Attempts : 1 1
          Sum: 2, Avg: 1, Min: 1, Max: 1]
      [GC Worker End Time (ms):  395.7  395.7]
      [Other:   1.8 ms]
   [Clear CT:   0.0 ms]
   [Other:   0.0 ms]
      [Choose CSet:   0.0 ms]
   [ 2408K->1453K(3584M)]
 [Times: user=0.01 sys=0.00, real=0.00 secs]
2013-12-26T22:48:30.682+0900: 0.749: [GC pause (young), 0.00347700 secs]
   [Parallel Time:   3.4 ms]
      [GC Worker Start Time (ms):  749.3  749.3]
      [Update RS (ms):  0.0  0.1
       Avg:   0.0, Min:   0.0, Max:   0.1]
         [Processed Buffers : 0 5
          Sum: 5, Avg: 2, Min: 0, Max: 5]
      [Ext Root Scanning (ms):  1.9  1.2
       Avg:   1.6, Min:   1.2, Max:   1.9]
      [Mark Stack Scanning (ms):  0.0  0.0
       Avg:   0.0, Min:   0.0, Max:   0.0]
      [Scan RS (ms):  0.0  0.2
       Avg:   0.1, Min:   0.0, Max:   0.2]
      [Object Copy (ms):  1.4  1.8
       Avg:   1.6, Min:   1.4, Max:   1.8]
      [Termination (ms):  0.0  0.0
       Avg:   0.0, Min:   0.0, Max:   0.0]
         [Termination Attempts : 1 1
          Sum: 2, Avg: 1, Min: 1, Max: 1]
      [GC Worker End Time (ms):  752.7  752.7]
      [Other:   0.0 ms]
   [Clear CT:   0.0 ms]
   [Other:   0.1 ms]
      [Choose CSet:   0.0 ms]
   [ 15M->2925K(3584M)]
 [Times: user=0.01 sys=0.00, real=0.00 secs]

G1GCでもEden領域、Survivor領域、Old領域の割合を変えて負荷をかけてみます。

  1. New:Old=1:1
  2. New:Old=1:2
  3. New:Old=1:4

  1. Eden:Su0:Su1=8:1:1
  2. Eden:Su0:Su1=4:1:1
  3. Eden:Su0:Su1=2:1:1

を組み合わせます。

パターン1. b×1(New:Old=1:2, Eden:Su0:Su1=8:1:1) MaxGCPauseMillis, G1HeapRegionSizeを指定しない


-XX:PermSize=128M -XX:MaxPermSize=128M
-Xms3584M -Xmx3584M
-Xloggc:/var/log/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps
-XX:+UseG1GC
-XX:MaxTenuringThreshold=32
-XX:NewRatio=2 -XX:SurvivorRatio=8 -XX:TargetSurvivorRatio=90

gc.g1gc1.t

パターン2. b×1(New:Old=1:2, Eden:Su0:Su1=8:1:1) MaxGCPauseMillis, G1HeapRegionSizeを指定


-XX:PermSize=128M -XX:MaxPermSize=128M
-Xms3584M -Xmx3584M
-Xloggc:/var/log/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps
-XX:+UseG1GC
-XX:MaxTenuringThreshold=32
-XX:NewRatio=2 -XX:SurvivorRatio=8 -XX:TargetSurvivorRatio=90
-XX:MaxGCPauseMillis=200
-XX:G1HeapRegionSize=8M

gc.g1gc13.t

パターン3. a×4(Old:New=1:1, Eden:Su0:Su1=2:1:1) MaxGCPauseMillis, G1HeapRegionSizeを指定しない


-XX:PermSize=128M -XX:MaxPermSize=128M
-Xms3584M -Xmx3584M
-Xloggc:/var/log/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps
-XX:+UseG1GC
-XX:MaxTenuringThreshold=32
-XX:NewRatio=1 -XX:SurvivorRatio=2 -XX:TargetSurvivorRatio=90

gc.g1gc8.t

私の実行した環境では、CMSアルゴリズムと比べてそれほどの性能優位性を感じませんでした。
※java6だからかも。
java7で検証したいところです。

2018-04-21 追記.
@kuro_m88 にご指摘頂き、 NewRatioのNew:Old比が逆になっているのを修正しました。
ご迷惑をおかけしました m(_ _*)m


こちらの動画で詳しいチューニングのやり方も解説しています。

スクリーンショット 2015-08-24 10.01.11

VisualVMでjavaプロセスを可視化してみよう part1