2.2.2 Javaヒープのチューニング
SerialGC使用時のJavaヒープのチューニングについて説明します。
一般的に,FullGCの方が,CopyGCよりも長い処理時間を要します。
GCの実行中はプログラムの処理が停止するため,FullGCの発生はシステムの処理性能に大きな影響を与えます。このため,New領域へのCopyGCの実施によって適切にメモリを回収して,Javaヒープ全体を対象とするFullGCの発生をできるだけ抑止することが,システムの停止回数の削減や,処理性能向上につながります。これを実現するためには,JavaVMオプションでそれぞれのメモリ空間のサイズや割合を適切に設定することが必要です。
これらを考慮した上で,JavaVMのチューニングは,次の2点を目的として実施します。
-
FullGCをできるだけ発生させないこと
-
FullGCの頻発を抑止し,不要なCopyGCを発生させないこと
理想的なメモリ使用量と経過時間の関係を次の図に示します。
|
この図の場合は,寿命の短いオブジェクトはすべてCopyGCによって回収できていて,オブジェクトの昇格や退避が発生しません。このため,CopyGC実行後のメモリサイズが一定です。これによって,FullGCが発生しない,安定した状態での運用を実現できます。
JavaVMのチューニングでは,この状態を理想として,JavaVMの使用するメモリ空間の各領域で使用するメモリサイズを見積もってチューニングします。
- ヒント
-
チューニングの目安
「図2-8」では,FullGCを一度も発生させない理想的な例を示しましたが,現実的にはFullGCが1回発生する間にCopyGCが10〜20回程度発生することを目安にして,チューニングしてください。
なお,Javaヒープのチューニングをした段階でJavaアプリケーションのテストを実施してください。Javaヒープのメモリを適切に見積もってもFullGCが頻発する場合は,Survivor領域があふれているなど,チューニングに問題がないか確認してください。
(1) Javaヒープのメモリサイズの見積もり
JavaVMのチューニングでは,JavaVM固有領域の各領域のメモリサイズを適切に見積もる必要があります。
見積もりのポイントになるメモリサイズは次のとおりです。
-
Javaヒープ全体のメモリサイズ
-
Tenured領域のメモリサイズ
-
Survivor領域のメモリサイズ
-
Eden領域のメモリサイズ
このほか,Metaspace領域も,必要に応じて見積もります。
CopyGC後の生存オブジェクトのサイズがSurvivor領域のサイズよりも大きい場合,Survivor領域があふれ,1度のCopyGCの実行でTenured領域に昇格するオブジェクトが発生します。また,Survivor領域のサイズが小さい場合に,Survivor領域の使用率が上がってくると,本来短寿命(CopyGC間隔以下,またはCopyGC間隔の1〜2倍程度の寿命)のJavaオブジェクトが,数回のCopyGCの実行でTenured領域に昇格してしまいます。
- ヒント
-
次に示す問題が確認できた場合は,Survivor領域の不足が原因でFullGCが発生しています。なお,拡張verbosegc情報とは,起動時に-XX:+HitachiVerboseGCオプションを設定しておくと,GC発生時にJavaVMログファイルに出力される情報です。
-
Tenured領域の増加要因が短寿命オブジェクトであると特定された場合
-
拡張verbosegc情報で,CopyGC時のSurvivor領域あふれが確認できた場合
-
-XX:+HitachiVerboseGCPrintTenuringDistribution指定時に出力される拡張verbosegc情報で,オブジェクトの昇格年齢が常に1であることが観測できた場合
これらの問題を回避するためには,-XX:SurvivorRatioオプションの設定値を小さくして,Eden領域とSurvivor領域の割合を最適化する必要があります。
-
このことを考慮し,見積もりでは,まず,Tenured領域のメモリサイズとNew領域のメモリサイズを算出して,それらを基にJavaヒープ全体のメモリサイズを算出します。
メモリサイズを見積もる順序を次の図に示します。図中の番号の順番で見積もりを実施します。
|
見積もり手順を示します。なお,番号は図中の番号と対応しています。
-
Tenured領域で使用するメモリサイズを見積もります。
見積もり方法については,「2.2.3 Javaヒープ内のTenured領域のメモリサイズの見積もり」を参照してください。
-
Survivor領域で使用するメモリサイズを見積もります。
見積もり方法については,「2.2.4(1) Javaヒープ内のSurvivor領域のメモリサイズの見積もり」を参照してください。
-
Eden領域で使用するメモリサイズを見積もります。
見積もり方法については,「2.2.4(2) Javaヒープ内のEden領域のメモリサイズの見積もり」を参照してください。
-
2.と3.の合計として,New領域全体のメモリサイズを算出します。
-
一定期間存在するオブジェクトの扱いを検討して,必要なメモリサイズをTenured領域またはNew領域のメモリサイズに追加します。
検討方法については,「2.2.5 Javaヒープ内に一定期間存在するオブジェクトの扱いの検討」を参照してください。
-
1.,4.および5.の合計として,Javaヒープ全体のメモリサイズを算出します。
-
必要に応じてMetaspace領域のメモリサイズを見積もります。
見積もり方法については,「2.2.7 Javaヒープ内のMetaspace領域のメモリサイズの見積もり」を参照してください。
(2) Javaヒープのメモリサイズの設定方法
見積もったメモリサイズは,「2.2.1(5) SerialGC使用時のJavaVMで使用するメモリ空間の構成とJavaVMオプション」で説明したオプションで指定します。それぞれの領域のメモリサイズの設定方法を次に示します。
- Javaヒープ全体のメモリサイズ
-
-Xmx<size>オプションで最大サイズを指定して,-Xms<size>オプションで初期サイズを指定します。
- Tenured領域のメモリサイズ
-
-XX:NewRatio=<value>オプションで,Javaヒープ全体に対する,Tenured領域とNew領域の分割比を指定します。例えば,「-XX:NewRatio=5」とした場合には,-Xmx<size>オプションで指定したメモリサイズが,次のように分割されます。
New領域のメモリサイズ:Tenured領域のメモリサイズ = 1:5
- Survivor領域のメモリサイズとEden領域のメモリサイズ
-
-XX:SurvivorRatio=<value>オプションで,New領域全体に対する,Survivor領域とEden領域の分割比を指定します。なお,分割比は,Survivor領域のFrom空間およびTo空間に対してEden領域を何倍確保するかの数値で指定します。例えば,「-XX:SurvivorRatio=2」とした場合には,-XX:NewRatio=<value>オプションで決まったNew領域のメモリサイズが,次のように分割されます。
Eden領域のメモリサイズ:From空間のメモリサイズ:To空間のメモリサイズ=2:1:1
- Metaspace領域のメモリサイズ
-
-XX:MaxMetaspaceSize=<size>オプションで最大サイズを指定して,-XX:MetaspaceSize=<size>オプションで初期サイズを指定します。
(3) Javaヒープのメモリサイズの使用状況の確認方法
それぞれのメモリサイズは,アプリケーションを実際に動作させて,メモリ使用量を測定しながらチューニングしていきます。日立JavaVMでは,-XX:+HitachiVerboseGCオプションを指定してアプリケーションを起動することで,GC実行時の各領域の詳細なメモリサイズを拡張verbosegc情報として出力できます。この出力内容を基にチューニングを実施してください。
拡張verbosegc情報として出力できる主な内容を次に示します。
-
GCの発生日時
-
GC種別
-
GC情報※1
-
GC経過時間
-
Eden情報※1
-
Survivor情報※1
-
Tenured情報※1
-
Metaspace領域情報※1
-
GC要因内容※2
- 注※1
-
各領域について,GC前後のメモリサイズおよび使用メモリサイズが出力されます。
- 注※2
-
-XX:+HitachiVerboseGCPrintCauseオプションが指定されている場合に出力されます。
拡張verbosegc情報の出力例とFullGCの要因分析方法については,「2.2.8 拡張verbosegc情報を使用したFullGCの要因の分析方法」を参照してください。また,オプションについては,「-XX:[+|-]HitachiVerboseGC(拡張verbosegc情報出力オプション)」を参照してください。
なお,日立JavaVMでは,javagcコマンドを使用して,任意のタイミングでFullGCを発生させることもできます。この場合,-vオプションを指定することで,拡張verbosegc情報と同じ内容を出力できます。javagcコマンドの詳細については,「javagc(GCの強制発生)」を参照してください。