ここでは、パフォーマンスを向上させるための Java ヒープのチューニングに関する項目について説明します。
最大ヒープサイズは、プロセスあたりの最大アドレス空間によって決まります。次の表に、各種プラットフォームのプロセスあたりの最大アドレス値を示します。
表 4–1 プロセスあたりの最大アドレス空間
オペレーティングシステム |
プロセスあたりの最大アドレス空間 |
Redhat Linux 32 bit |
2G バイト |
Redhat Linux 64 bit |
3G バイト |
Windows 98/2000/NT/Me/XP |
2G バイト |
Solaris x86 (32 bit) |
4G バイト |
Solaris 32 bit |
4G バイト |
Solaris 64 bit |
テラバイト |
プロセスにもスタックやライブラリなどのための領域が必要なため、最大ヒープスペースは常に、プロセスあたりの最大アドレス空間よりも小さくなります。割り当て可能な最大ヒープスペースを決定するには、プロファイリングツールを使用して、メモリーがどのように使用されているかを調べます。プロセスで使用される最大スタック空間と、ライブラリやその他のメモリー構造で使用されるメモリー量を測定します。最大アドレス空間とこれらの値の合計の差が、ヒープに割り当て可能なメモリー量になります。
ヒープサイズを大きくしたり、別のガベージコレクタを使用したりすることで、パフォーマンスを改善できます。一般に、長時間実行されるサーバーアプリケーションでは、複数のプロセッサを持つマシンで J2SE スループットコレクタを使用し ( -XX:+AggressiveHeap)、ヒープサイズはマシンの空きメモリーをできるだけ多く使用した大きさにします。
次の JVM パラメータでヒープサイズを制御できます。
-Xmsvalue
-Xmxvalue
-XX:MinHeapFreeRatio=minimum
-XX:MaxHeapFreeRatio=maximum
-XX:NewRatio=ratio
-XX:NewSize=size
-XX:MaxNewSize=size
-XX:+AggressiveHeap
-Xms パラメータと -Xmx パラメータはそれぞれ、最小ヒープサイズと最大ヒープサイズを定義します。世代がいっぱいになると GC が行われるので、スループットは使用可能メモリーの量に反比例します。デフォルトでは、JVM はコレクションのたびにヒープサイズを拡大または縮小して、ライブオブジェクトに対する空き容量の割合を一定の範囲内に維持しようとします。この範囲は、-XX:MinHeapFreeRatio=minimum パラメータと -XX:MaxHeapFreeRatio=maximum パラメータでパーセント値として設定され、合計サイズは -Xms と -Xmx によって制限されます。
ヒープサイズを固定する場合は、-Xms と -Xmx に同じ値を設定します。ヒープサイズが拡大または縮小されると、JVM では、事前に定義された NewRatio を維持するために、古い世代と新しい世代のサイズが再計算されます。
NewSize パラメータと MaxNewSize パラメータは、新しい世代の最小サイズと最大サイズを制御します。新しい世代のサイズを固定するには、これらのパラメータに同じ値を設定します。若い世代を大きくするほど、マイナーコレクションの発生回数は少なくなります。古い世代に対する若い世代のサイズの比率は、NewRatio で制御されます。たとえば、-XX:NewRatio=3 と設定すると、古い世代と若い世代の比率は 1:3 になり、Eden 領域と Survivor 領域の合計サイズはヒープサイズの 4 分の 1 になります。
デフォルトでは、Application Server は Java HotSpot Server JVM によって起動されます。Server JVM のデフォルトの NewRatio は 2 で、古い世代がヒープの 3 分の 2、新しい世代が 3 分の 1 を占めます。新しい世代を大きくするほど、生存期間の短いオブジェクトを数多く収容できるので、時間のかかるメジャーコレクションの回数が減ります。同時に、古い世代も、生存期間の長いオブジェクトを多数保持するのに十分な大きさがあります。
Java ヒープのサイズを決定するには、次の手順に従います。
JVM に割り当てることができるメモリーの総量を決定します。それに応じて、若い世代のサイズに対する独自のパフォーマンスメトリックをグラフにして、最適な設定を求めます。
若い世代に十分なメモリーを割り当てます。デフォルト値は、NewRatio と -Xmx の設定から計算されます。
Eden 領域または若い世代の領域を大きくするほど、フル GC の実行間隔は長くなります。ただし、それに比例して若い世代でのコレクションに時間がかかる可能性があります。一般に、Eden のサイズは最大ヒープサイズの 4 分の 1 から 3 分の 1 に設定します。古い世代は新しい世代よりも大きくしてください。
最新のデフォルト設定については、「Java HotSpot VM Options」を参照してください。
Solaris 上の Application Server で大きなアプリケーションに対して使用されるヒープ設定の例を次に示します。
-Xms3584m -Xmx3584m -verbose:gc -Dsun.rmi.dgc.client.gcInterval=3600000
SurvivorRatio パラメータは、2 つの Survivor 領域のサイズを制御します。たとえば -XX:SurvivorRatio=6 と設定すると、各 Survivor 領域と Eden 領域の比率は 1:6 になり、それぞれの Survivor 領域のサイズは若い世代全体の 8 分の 1 になります。Solaris のデフォルトは 32 です。Survivor 領域が小さすぎると、コピーコレクションがオーバーフローし、その分が直ちに古い世代に送られてしまいます。Survivor 領域が大きすぎると、無意味になります。GC のたびに、JVM は、寿命の長い (tenured) 世代に送るまでにオブジェクトをコピーできる回数 (tenure しきい値と呼ばれる) を判断します。このしきい値は、Survivor 領域の使用量が半分に維持されるように選択されます。
このしきい値と、新しい世代のオブジェクトのコピー回数を表示するには、-XX:+PrintTenuringDistribution オプションを使用します。これは、アプリケーションの生存期間の分布を観測するのに役立ちます。