さて、前回までで、ログからフルGC(及び高負荷なコンカレントGC)が発生していることはわかりました。
で、このチューニングの目的は、GCによるマシンへの負担を低減することにあります。
まず「なぜGCが発生するのか?」と「jvmがどうやってメモリ管理を行っているのか?」を知らないとチューニングのしようがないのですが、これらについては、下記に詳しく記載されていたのでリンクをはっておきます。
- GC発生の要因 http://www.atmarkit.co.jp/ait/articles/1005/13/news095.html
- HotSpot JVMのヒープ領域について http://gihyo.jp/dev/serial/01/jvm-arc/0007
で、これらをふまえて・・・
一口にチューニングと言ってもいくつかのアプローチをとることができます。
- とにかくOld領域がいっぱいにならないようにマイナーGC回数を増やしてNew領域でやりくりする
- フルGCが発生するのはしようがないと諦めて、フルGC発生時の停止時間を短くする
- GCアルゴリズムを変更する(第4回でやります)
上記3つのそれぞれの方向からアプローチしてみます。
3-1. とにかくOld領域がいっぱいにならないように・・・
ということで、Eden領域、Survivor領域、Old領域の割合をいろいろ変えてみます。
- New:Old=1:1
- New:Old=1:2
- New:Old=1:4
と
- Eden:Su0:Su1=8:1:1
- Eden:Su0:Su1=4:1:1
- Eden:Su0:Su1=2:1:1
の組み合わせでいろいろ試してみました。
実際に渡したパラメータはこんな感じ。
パターン1. b×1(New:Old=1:2, Eden:Su0:Su1=8:1:1)
-XX:PermSize=128M -XX:MaxPermSize=128M
-Xms3584M -Xmx3584M
-Xloggc:/var/log/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps
-XX:+UseConcMarkSweepGC
-XX:MaxTenuringThreshold=32
-XX:NewRatio=2 -XX:SurvivorRatio=8 -XX:TargetSurvivorRatio=90
パターン2. a×1(New:Old=1:1, Eden:Su0:Su1=8:1:1)
-XX:PermSize=128M -XX:MaxPermSize=128M
-Xms3584M -Xmx3584M
-Xloggc:/var/log/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps
-XX:+UseConcMarkSweepGC
-XX:MaxTenuringThreshold=32
-XX:NewRatio=1 -XX:SurvivorRatio=8 -XX:TargetSurvivorRatio=90
パターン3. a×3(New:Old=1:1, Eden:Su0:Su1=2:1:1)
-XX:PermSize=128M -XX:MaxPermSize=128M
-Xms3584M -Xmx3584M
-Xloggc:/var/log/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps
-XX:+UseConcMarkSweepGC
-XX:MaxTenuringThreshold=32
-XX:NewRatio=1 -XX:SurvivorRatio=2 -XX:TargetSurvivorRatio=90
指定したパラメータの内容については下記のブログに詳しく載っていました。
また余談ですが、負荷は
- Jmeter http://jmeter.apache.org/
でかけました。
上記3つをピックアップしてみましたが、それぞれ波形が違うことが見て取れます。
maxのフルGC時間で比較すると、a×1(Old:New=1:1, Eden:Su0:Su1=8:1:1)が比較的良好なことがわかります。
3-2. フルGCが発生するのはしようがないと諦めて、フルGC発生時の停止時間を短くする
そもそも、フルGCの発生を完全に抑制することはできません。
なので、3-2.ではフルGCが発生するのはしようがないと割り切り、停止時間を短くするアプローチを取ってみます。
フルGCをインクリメンタルモードで動かしてみます。
イマドキのマシンは複数コア搭載が当たり前なので、アプリケーションを動かしていないコアをGCに使います。
また、大量のGCを一気に行うのではなく、細切れにちょびっとづつGCを行い、停止時間を短くします。
パラメータ例:
-XX:PermSize=128M -XX:MaxPermSize=128M
-Xms3584M -Xmx3584M
-Xloggc:/var/log/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps
-XX:+UseConcMarkSweepGC
-XX:MaxTenuringThreshold=32
-XX:NewRatio=2 -XX:SurvivorRatio=8 -XX:TargetSurvivorRatio=90
-XX:+UseParNewGC
-XX:+CMSClassUnloadingEnabled
-XX:+CMSPermGenSweepingEnabled
-XX:+CMSIncrementalMode
-XX:+CMSIncrementalPacing
-XX:CMSIncrementalDutyCycleMin=0
-XX:+CMSParallelRemarkEnabled
で、インクリメンタルモードでも
- New:Old=1:1
- New:Old=1:2
- New:Old=1:4
と
- Eden:Su0:Su1=8:1:1
- Eden:Su0:Su1=4:1:1
- Eden:Su0:Su1=2:1:1
の組み合わせを試してみました。
インクリメンタルモードを指定したときと、そうでないとで波形に大きな違いがあることがわかります。
次回、GCアルゴリズムの変更を試してみます。
GCアルゴリズムを変えてみるへ
2018-04-21 追記.
@kuro_m88 にご指摘頂き、 NewRatioのNew:Old比が逆になっているのを修正しました。
ご迷惑をおかけしました m(_ _*)m
こちらの動画で詳しいチューニングのやり方も解説しています。
VisualVMでjavaプロセスを可視化してみよう part1