Hitachi

Cosminexus V11 アプリケーションサーバ システム設計ガイド


7.6.1 Javaヒープ内のSurvivor領域のメモリサイズの見積もり

Survivor領域のメモリサイズは,実際にアプリケーションを動作させて,Survivor領域の使用状況を確認しながらチューニングしていきます。

チューニングの流れを次に示します。

  1. アプリケーションでのリクエスト/レスポンス処理に使用するメモリサイズを見積もり,それをSurvivor領域のメモリサイズに指定して,アプリケーションを実行します。

    このとき,チューニングで使用する情報を出力するために,-XX:+HitachiVerboseGCPrintTenuringDistributionオプションを指定してJ2EEサーバを起動します。

  2. Survivor領域に割り当てられているメモリサイズと,アプリケーション実行時に実際に使用されているメモリ使用量から,メモリ使用率を確認します。

    メモリ使用率が100%に近い場合,CopyGC実行時にNew領域およびSurvivor領域のFrom空間の使用中オブジェクトがTo空間に入り切らなくなり,オブジェクトの退避が発生します。この場合は,Survivor領域を増やすことを検討してください。

  3. Survivor領域のオブジェクトの年齢分布を確認します。

    Survivor領域のメモリサイズを増やしたり,昇格するためのしきい値を上げたりすることで,オブジェクトが昇格するのが遅くなります。寿命の長いオブジェクトをSurvivor領域に格納し続けるのは,性能を低下させる要因になります。

    逆に,Survivor領域のメモリサイズを減らしたり,昇格するためのしきい値を下げたりすることで,オブジェクトが昇格するのが早くなります。ただし,寿命の短いオブジェクトが昇格するのは,FullGCの発生頻度を増やす要因になります。

    Survivor領域のメモリサイズと昇格のしきい値は,この二つのバランスを取るように検討してください。

それぞれのチューニング作業について説明します。

〈この項の構成〉

(1) リクエスト/レスポンス処理に使用するメモリサイズの見積もり

Survivor領域は,寿命の短いオブジェクトを格納する領域です。サーバサイドで動作するアプリケーションの場合,リクエストやレスポンスを処理するために使われている,寿命の短いオブジェクトを格納する領域と考えることができます。このため,Survivor領域のメモリサイズの見積もりでは,ある時点で存在する寿命が短いオブジェクトの最大サイズ,つまり,ある時点でのリクエストやレスポンスの処理に使用するメモリの最大サイズを考えます。例えば,ステートレスなサーブレットで構成されたアプリケーションの場合,Survivor領域のメモリサイズを,「一つのリクエスト処理で使用する最大メモリサイズ×リクエストの同時実行数」と考えることができます。

(2) メモリ使用率の確認

7.6.1(1) リクエスト/レスポンス処理に使用するメモリサイズの見積もり」で見積もった値をSurvivor領域のメモリサイズとして設定して,アプリケーションを実行します。実行時に使用されるメモリ使用量から,Survivor領域に割り当てたメモリサイズに対するメモリ使用率を確認します。

参考

Survivor領域のメモリサイズは,直接は指定できません。

Survivor領域のメモリサイズを指定する場合は,まず,-XmxオプションでJavaヒープの最大サイズを指定して,-XX:NewRatio=<value>によってJavaヒープのメモリサイズをNew領域とTenured領域で分ける割合を指定した上で,-XX:SurvivorRatio=<value>オプションによって,Eden領域との割合を指定する必要があります。

メモリ使用率は,拡張verbosegc情報で確認できます。

CopyGC実行時の拡張verbosegc情報の出力例を次に示します。

…
[VGC]<Wed May 11 23:12:05 2005>[GC 27340K->27340K(32704K), 0.0432900 secs][DefNew::Eden: 3440K->0K(3456K)][DefNew::Survivor: 58K->64K(64K)][Tenured: 23841K->27282K(29184K)][Metaspace: 3634K(4492K, 4492K)->3634K(4492K, 4492K)][class space: 356K(388K, 388K)->356K(388K, 388K)][cause:ObjAllocFail][User: 0.0156250 secs][Sys: 0.0312500 secs]
…

「DefNew::Survivor: 58K->64K(64K)」は,「GC実行前のメモリサイズ->GC実行後のメモリサイズ(割り当てられているメモリサイズ)」を意味します。この場合,64キロバイトのSurvivor領域中64キロバイトがすでに使用されていて,使用率は100%になります。これは,CopyGCで,To空間のメモリサイズが不足し,Javaオブジェクトの退避が行われたことを示しています。退避では,Tenured領域に本来格納されないはずの寿命の短いオブジェクトが格納され,FullGCの発生頻度を増やす要因になります。このような場合は,Survivor領域のメモリサイズを増やすことを検討してください。新しいSurvivor領域のメモリサイズは次のように見積もります。

新しいSurvivor領域のメモリサイズ
=現在のSurvivor領域のメモリサイズ+退避されたJavaオブジェクトの合計サイズ

「退避されたJavaオブジェクトの合計サイズ」は,GC実行後のTenured領域メモリの増加サイズで近似できます。この例では,「Tenured: 23841K->27282K(29184K)」が,Tenured領域メモリの増加サイズを示していて,27,282キロバイト-23,841キロバイト=3,441キロバイトとなります。

また,Survivor領域のメモリサイズを増加させると,Javaオブジェクトの昇格のしきい値が上がり,昇格しにくくなります。詳細については,「7.6.1(3) オブジェクトの年齢分布の確認と見積もり」を参照してください。その結果,CopyGCで回収されないJavaオブジェクトが増えることがあるため,-XX:TargetSurvivorRatio=<value>を次のように見積もり,設定してください。

新しい-XX:TargetSurvivorRatio=<value>
=現在の-XX:TargetSurvivorRatio=<value>×(現在のSurvivor領域のメモリサイズ/新しいSurvivor領域のメモリサイズ)

(3) オブジェクトの年齢分布の確認と見積もり

Survivor領域のオブジェクトの年齢分布を確認し,寿命の長いオブジェクトが存在し続けていないか,または寿命の短いオブジェクトが昇格していないかを確認します。オブジェクトの年齢分布は,-XX:+HitachiVerboseGCPrintTenuringDistributionオプションの出力結果で確認できます。

J2EEサーバ起動時にusrconf.cfgに-XX:+HitachiVerboseGCPrintTenuringDistributionオプションを指定すると,Survivor領域の使用状況がCopyGC発生のタイミングでJavaVMログファイルに出力されます。出力例を次に示します。

[PTD]<Wed May 28 11:45:23 2008>[Desired survivor:5467547 bytes][New  threshold:3][MaxTenuringThreshold:31][age1:1357527][age2:1539661]

「New threshold:」に続けて出力されているのが,次のCopyGCで昇格するJavaオブジェクトの最低の年齢です。例の場合は,次回のCopyGCで,年齢が3歳以上のJavaオブジェクトが昇格します。「age<数値>:」に続けて出力されているのが,Survivor領域で1歳からその年齢までのJavaオブジェクトが使用しているメモリサイズの累計です。例の場合は,1歳のJavaオブジェクトのメモリサイズが1,357,527バイト,1歳と2歳のJavaオブジェクトのメモリサイズの累計が1,539,661バイトであることを示しています。また,累計から逆算することで,2歳のJavaオブジェクトのメモリサイズが182,134(1,539,661-1,357,527)バイトであることがわかります。一般的に,Survivor領域のオブジェクトの年齢分布は次に示すグラフのようになります。

図7‒12 Survivor領域のオブジェクトの年齢分布

[図データ]

グラフの「サイズ」は,ある「年齢」のJavaオブジェクトの合計のサイズです。また,「累計サイズ」は,ある「年齢」までのJavaオブジェクトの合計のサイズです。

このグラフでは,Survivor領域のJavaオブジェクトが占めるサイズが,年齢が上がるに連れて減少しています。また,1歳年齢が上がったときに減少するサイズは年齢が若いほど大きくなります。このことから,次のことがわかります。

この例では,6歳以上のJavaオブジェクトはほとんどCopyGCで回収されていません。そのため,Javaオブジェクトの昇格のしきい値が7歳以上の場合,回収される可能性が低いJavaオブジェクトに対してCopyGCを行うことになり,性能を低下させる要因になります。逆に,Javaオブジェクトの昇格のしきい値が2歳以下の場合,CopyGCで回収される可能性の高いオブジェクトが昇格することになり,FullGCの発生が増す要因になります。この例では,昇格のしきい値を5〜6歳程度とするのが,バランスの取れたしきい値となります。

グラフの傾きはシステムによって異なるため,Survivor領域のオブジェクトの年齢分布を確認し,システムごとの最適な昇格年齢を決定することが重要です。

参考

Javaオブジェクトの昇格のしきい値は,CopyGCごとに動的に変更され,-XX:MaxTenuringThreshold=<value>オプションと,Survivor領域のメモリサイズおよび-XX:TargetSurvivorRatio=<value>オプションに設定した値を基に決まります。-XX:MaxTenuringThreshold=<value>は,昇格のしきい値の最大の年齢です。Javaオブジェクトの年齢がこの値を超えると,必ず昇格します。-XX:TargetSurvivorRatio=<value>は,Survivor領域のメモリ使用率の目標値です。JavaVMは,Survivor領域のメモリ使用率が,できるだけこの値に近くなるように,昇格のしきい値を決定します。具体的には,CopyGCが終了した時の1歳からn歳までのJavaオブジェクトの累計サイズが,目標の使用率となるnを探し,次のCopyGCの昇格のしきい値をnにします。-XX:TargetSurvivorRatio=<value>のデフォルト値は50%です。Survivor領域のメモリ使用率が大きいほどSurvivor領域を有効に利用できますが,Survivor領域の空きに余裕がなくなるため,オブジェクトの退避が発生しやすくなります。