ここでは、Javaヒープとガーベジコレクション(GC)を説明します。
Javaヒープは、Javaプロセス内に存在するJavaオブジェクトを格納するための領域です。
Javaヒープは、New世代領域、Old世代領域およびPermanent世代領域に大別され、Java VMが管理・制御します。なお、New世代領域とOld世代領域は、メモリ割り当てプールという形で各領域を合わせて一括的に管理・制御します。
Java VMは、Javaアプリケーションの実行時に、JavaオブジェクトをJavaヒープの各領域に格納します。Javaヒープの空き容量がなくなった場合は、java.lang.OutOfMemoryErrorがスローされます。
また、不要となったJavaオブジェクトはGCにより回収され、Javaヒープの空き領域が増加します。なお、New世代領域に存在する不要となったJavaオブジェクトを回収するGC処理を「NewGC処理(またはマイナーGC処理)」といいます(NewGCまたはマイナーGCと略す場合もあります)。また、New世代領域だけでなく、Old世代領域やPermanent世代領域を含むJavaヒープ全体に存在する不要となったJavaオブジェクトを回収するGC処理を「FullGC処理」といいます(FullGCと略す場合もあります)。
New世代領域とOld世代領域(メモリ割り当てプール)
New世代領域とOld世代領域は、インスタンスや配列などのJavaオブジェクトを管理する領域です。
New世代領域は寿命の短いJavaオブジェクトを管理します。通常、Javaアプリケーションで要求されたオブジェクトは、New世代領域に生成されます。一定期間New世代領域で生存したJavaオブジェクトは、GC処理によってOld世代領域に移動されます。そして、Old世代領域に存在する不要となったJavaオブジェクトは、FullGC処理によって回収されます。領域がNew世代とOld世代に分かれるのは、世代別にGC処理を実行するためです。
なお、メモリ割り当てプール全体のサイズの初期値および最大値は、それぞれ、“-Xms”オプションおよび“-Xmx”オプションで指定することができます。
Permanent世代領域
Permanent世代領域は、Javaのクラス、メソッドや定数など、永続的に参照される静的オブジェクトを管理する領域です。
Permanent世代領域に存在する不要となったオブジェクトは、FullGC処理によって回収されます。
なお、Permanent世代領域の大きさの初期値および最大値は、それぞれ、“-XX:PermSize”オプションおよび“-XX:MaxPermSize”オプションで指定することができます。
参考
ユーザ空間
Javaヒープは、Javaプロセスのユーザ空間内に割り当てられます。
また指定された最大値までJavaヒープが利用できる環境にするため、Java VM起動時に、メモリ割り当てプールおよびPermanent世代領域を、各最大値の大きさで連続領域としてリザーブします(同一プロセス内の他の処理から使用できないようにします)。
このため、Javaヒープの最大値を大きく設定すると、その分だけスタックなど他の処理に割り当てられるメモリ領域が減少します。
別な言い方をすれば、ユーザ空間内で使用できるメモリ量の上限から、Javaアプリケーション自身、Java VM、そしてネイティブモジュール(OSを含む)が使用するメモリ量などを差し引いた値が、Javaヒープとして使用できる上限となります。
なおJava VM起動時に指定されたJavaヒープの大きさが利用できない場合、Java VMは以下のメッセージを出力し、Javaプロセスを終了させます。
Error occurred during initialization of VM |
このメッセージが出力された場合は、Javaヒープを小さくするチューニングを行ってください。
またSolaris用およびLinux用のJava VMにおいて、Java VM起動時またはJavaアプリケーション実行中に「ユーザ空間不足」または「仮想メモリ不足」が発生した場合、Java VMは以下のメッセージを出力し、Javaプロセスを終了させます。
Java VM起動時の場合
制御名: mmap failed: errno=エラー情報, 制御情報.... |
Javaアプリケーション実行中の場合
制御名: mmap failed: errno=エラー情報, 制御情報.... |
メモリ不足が発生した際のJava VMの制御名
メモリ不足が発生した際のJava VMのエラー情報
メモリ不足が発生した際のJava VMの制御情報
ユーザ空間が不足している場合は、Javaヒープを小さくするチューニングを行ってください。
仮想メモリが不足している場合は、他の不要なプロセスを終了して仮想メモリに余裕を持たせるか、物理メモリ(RAM)またはスワップファイルを拡張して仮想メモリを増やすようにチューニングを行ってください。
参考
Javaヒープにメモリを割り当てるタイミング
Java VMは、OSの仮想メモリ資源を効率的に利用するために、起動時にJavaヒープの各領域の初期値を割り当て、各領域の最大値まで段階的に拡大する制御方法を用いています。
具体的には、Javaプロセスの起動時はメモリ割り当てプールおよびPermanent世代領域の各初期値のサイズを割り当てます。その後、各領域のサイズは各領域の最大値までの範囲内で、GC処理後や、オブジェクトの生成時のタイミングで、その時々の最適な値に自動調整されます(GC処理としてメモリ割り当てプールの最小使用量保証機能を無効にしたパラレルGCを使用している場合は、メモリ割り当てプールの大きさが、初期値よりも小さくなる場合があります)。
参考
スワップ発生時の対処方法
各領域を拡大していく過程で、OS側で物理メモリの資源をディスクにスワップする処理が発生する場合があります。このスワップ処理により、各領域を拡大するFullGC処理に時間がかかる場合があります。FullGC処理中のスワップ処理発生による時間遅延が問題になる場合は、Javaヒープの各領域に対する初期値と最大値は同じ値に指定してください。
パラレルGCについては、“9.2.3 New世代領域用制御処理並列化機能付きGC(パラレルGC)”を参照してください。