ここでは、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ヒープの空き領域が増加します。なお、Old世代領域やPermanent世代領域に存在する不要となったJavaオブジェクトを回収するGC処理を、特にFullGC処理といいます。
図1 Javaヒープの構造
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 VM起動時に指定されたJavaヒープの大きさが利用できない場合、Java VMは以下のメッセージを出力し、Javaプロセスを終了させます。
Error occurred during initialization of VM |
このメッセージが出力された場合は、Javaヒープを小さくするチューニングを行ってください。
またSolaris用およびLinux用のJava VMにおいて、Java VM起動時に「ユーザ空間不足」または「仮想メモリ不足」が発生した場合、Java VMは以下のメッセージを出力し、Javaプロセスを終了させます。
制御名: mmap failed: errno=エラー情報, 制御情報.... |
制御名 : メモリ不足が発生した際のJava VMの制御名
エラー情報:メモリ不足が発生した際のJava VMのエラー情報
制御情報 : メモリ不足が発生した際のJava VMの制御情報
ユーザ空間が不足している場合は、Javaヒープを小さくするチューニングを行ってください。
仮想メモリが不足している場合は、他の不要なプロセスを終了して仮想メモリに余裕を持たせるか、物理メモリ(RAM)またはスワップファイルを拡張して仮想メモリを増やすようにチューニングを行ってください。
ユーザ空間
Java VMは、OSの仮想メモリ資源を効率的に利用するために、起動時にJavaヒープの各領域の初期値を割り当て、各領域の最大値まで段階的に拡大する制御方法を用いています。
具体的には、Javaプロセスの起動時はメモリ割り当てプールおよびPermanent世代領域の各初期値のサイズを割り当てます。その後、FullGC処理の結果、各領域が不足した場合、各領域の最大値まで、段階的に拡大していきます。
なお、各領域を拡大していく過程で、OS側で物理メモリの資源をディスクにスワップする処理が発生する場合があります。このスワップ処理により、各領域を拡大するFullGC処理に時間がかかる場合があります。FullGC処理中のスワップ処理発生による時間遅延が問題になる場合は、Javaヒープの各領域に対する初期値と最大値は同じ値に指定してください。
■ガーベジコレクションのログ
GCのログを採取する方法は、“7.3.1 ガーベジコレクションのログ出力”を参照してください。
■ガーベジコレクション処理の実行抑止
図2に示す機能を使用するJavaアプリケーションを実行すると、Javaヒープ内に存在する全オブジェクトの移動が禁止されるクリティカルセクションと呼ばれる状態が、当該機能の利用状況に応じて発生します。
クリティカルセクション状態発生時は、オブジェクトの移動が禁止されるため、オブジェクト移動が必須となるGC処理の実行が抑止される状態となります。
Java VMは、JavaアプリケーションによりGC処理の実行が抑止されている際に発生したオブジェクト生成要求に対し、New世代領域に空きが無い場合には、Old世代領域の空き領域を一時的に使用して対応します。
そして、要求されたオブジェクト量を満たす空きがOld世代領域にない場合には、java.lang.OutOfMemoryError例外を発生させます。
そのため図2の機能を多用するJavaアプリケーションの場合は、GC処理実行が抑止される状態も多数発生する可能性が高くなり、当該機能を多用しないJavaアプリケーションに比べ、GC処理実行抑止に因るjava.lang.OutOfMemoryError例外が発生しやすい状態にあります。
特にOld世代領域が小さい状態でJavaアプリケーションを実行した場合は、Old世代領域の空きとなり得る最大値(仮にOld世代領域を全く使用しない場合だと、Old世代領域自身の大きさ)もその大きさに比例して小さいため、その傾向が強まることがあります。
なおFJVMでは、GC処理の実行抑止によりjava.lang.OutOfMemoryError例外が発生したかどうかの情報を出力する機能を提供しています。
詳しくは“7.2.5 メモリ領域不足事象発生時のメッセージ出力機能の強化”を参照してください。
図2 ガーベジコレクション処理の実行が抑止される機能
【GC処理の実行が抑止される状態となるJNI関数】
- GetPrimitiveArrayCritical()実行からReleasePrimitiveArrayCritical()実行までの間 |
【GC処理の実行が抑止される状態となるJVMPI関数】
- DisableGC()実行からEnableGC()実行までの間 |
【GC処理の実行が抑止される状態となるJVMPIイベント】
- JVMPI_EVENT_THREAD_START |