Survivor領域のメモリサイズは,実際にアプリケーションを動作させて,Survivor領域の使用状況を確認しながらチューニングしていきます。
チューニングの流れを次に示します。
それぞれのチューニング作業について説明します。
Survivor領域は,寿命の短いオブジェクトを格納する領域です。サーバサイドで動作するアプリケーションの場合,リクエストやレスポンスを処理するために使われている,寿命の短いオブジェクトを格納する領域と考えることができます。このため,Survivor領域のメモリサイズの見積もりでは,ある時点で存在する寿命が短いオブジェクトの最大サイズ,つまり,ある時点でのリクエストやレスポンスの処理に使用するメモリの最大サイズを考えます。例えば,ステートレスなサーブレットで構成されたアプリケーションの場合,Survivor領域のメモリサイズを,「一つのリクエスト処理で使用する最大メモリサイズ×リクエストの同時実行数」と考えることができます。
「(1) リクエスト/レスポンス処理に使用するメモリサイズの見積もり」で見積もった値をSurvivor領域のメモリサイズとして設定して,アプリケーションを実行します。実行時に使用されるメモリ使用量から,Survivor領域に割り当てたメモリサイズに対するメモリ使用率を確認します。
メモリ使用率は,拡張verbosegc情報で確認できます。
コピーガーベージコレクション実行時の拡張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)][Perm: 1269K->1269K(4096K)][cause:ObjAllocFail][User: 0.0156250 secs][Sys: 0.0312500 secs] … |
「DefNew::Survivor: 58K->64K(64K)」は,「ガーベージコレクション実行前のメモリサイズ->ガーベージコレクション実行後のメモリサイズ(割り当てられているメモリサイズ)」を意味します。この場合,64キロバイトのSurvivor領域中64キロバイトがすでに使用されていて,使用率は100%になります。これは,コピーガーベージコレクションで,To空間のメモリサイズが不足し,Javaオブジェクトの退避が行われたことを示しています。退避では,Tenured領域に本来格納されないはずの寿命の短いオブジェクトが格納され,フルガーベージコレクションの発生頻度を増やす要因になります。このような場合は,Survivor領域のメモリサイズを増やすことを検討してください。新しいSurvivor領域のメモリサイズは次のように見積もります。
新しいSurvivor領域のメモリサイズ =現在のSurvivor領域のメモリサイズ+退避されたJavaオブジェクトの合計サイズ |
「退避されたJavaオブジェクトの合計サイズ」は,ガーベージコレクション実行後のTenured領域メモリの増加サイズで近似できます。この例では,「Tenured: 23841K->27282K(29184K)」が,Tenured領域メモリの増加サイズを示していて,27,282キロバイト-23,841キロバイト=3,441キロバイトとなります。
また,Survivor領域のメモリサイズを増加させると,Javaオブジェクトの昇格のしきい値が上がり,昇格しにくくなります。詳細については,「(3) オブジェクトの年齢分布の確認と見積もり」を参照してください。その結果,コピーガーベージコレクションで回収されないJavaオブジェクトが増えることがあるため,-XX:TargetSurvivorRatio=<value>を次のように見積もり,設定してください。
新しい-XX:TargetSurvivorRatio=<value> =現在の-XX:TargetSurvivorRatio=<value>×(現在のSurvivor領域のメモリサイズ/新しいSurvivor領域のメモリサイズ) |
Survivor領域のオブジェクトの年齢分布を確認し,寿命の長いオブジェクトが存在し続けていないか,または寿命の短いオブジェクトが昇格していないかを確認します。オブジェクトの年齢分布は,-XX:+HitachiVerboseGCPrintTenuringDistributionオプションの出力結果で確認できます。
J2EEサーバ起動時にusrconf.cfgに-XX:+HitachiVerboseGCPrintTenuringDistributionオプションを指定すると,Survivor領域の使用状況がコピーガーベージコレクション発生のタイミングで日立JavaVMログファイルに出力されます。出力例を次に示します。
[PTD]<Wed May 28 11:45:23 2008>[Desired survivor:5467547 bytes][New threshold:3][MaxTenuringThreshold:31][age1:1357527][age2:1539661] |
「New threshold:」に続けて出力されているのが,次のコピーガーベージコレクションで昇格するJavaオブジェクトの最低の年齢です。例の場合は,次回のコピーガーベージコレクションで,年齢が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オブジェクトはほとんどコピーガーベージコレクションで回収されていません。そのため,Javaオブジェクトの昇格のしきい値が7歳以上の場合,回収される可能性が低いJavaオブジェクトに対してコピーガーベージコレクションを行うことになり,性能を低下させる要因になります。逆に,Javaオブジェクトの昇格のしきい値が2歳以下の場合,コピーガーベージコレクションで回収される可能性の高いオブジェクトが昇格することになり,フルガーベージコレクションの発生が増す要因になります。この例では,昇格のしきい値を5~6歳程度とするのが,バランスの取れたしきい値となります。
グラフの傾きはシステムによって異なるため,Survivor領域のオブジェクトの年齢分布を確認し,システムごとの最適な昇格年齢を決定することが重要です。