この項では、ガベージ・コレクションに影響するその他の状況について説明します。
一部のアプリケーションは、ファイナライズと弱参照、ソフト参照またはファントム参照を使用してガベージ・コレクションと相互作用します。これらの機能は、Javaプログラミング言語のレベルでパフォーマンスに悪影響を与える可能性があります。たとえば、ファイナライズによってファイル記述子を閉じると、外部リソース(記述子)がガベージ・コレクションの機敏性に依存するようになります。ガベージ・コレクションにメモリー以外のリソースを管理させることは、通常はお薦めできません。
「はじめに」の「関連ドキュメント」では、ファイナライズの落とし穴とその回避方法をいくつか詳しく説明しています。
アプリケーションがガベージ・コレクションと相互作用できるもう1つの方法は、System.gc()
をコールして、すべてのガベージ・コレクションを明示的に呼び出すやり方です。これを行うと、必要ない場合(たとえば、マイナー・コレクションで十分な場合)でもメジャー・コレクションの実行が強制されるので、通常は使用しないことをお薦めします。明示的なガベージ・コレクションによるパフォーマンスへの影響を測定するには、フラグ-XX:+DisableExplicitGC
を使用してVMにSystem.gc()
のコールを無視させ、明示的なガベージ・コレクションを無効にします。
明示的なガベージ・コレクションが最もよく使用される例の1つが、リモート・メソッド呼出し(RMI)の分散ガベージ・コレクション(DGC)です。RMIを使用するアプリケーションは、他の仮想マシンのオブジェクトを参照します。これらの分散アプリケーションでは、ローカル・ヒープのガベージ・コレクションを時々呼び出さないとガベージを収集できないので、RMIがフル・コレクションを定期的に実行します。これらのコレクションの実行間隔はプロパティで制御できます(次の例を参照)。
java -Dsun.rmi.dgc.client.gcInterval=3600000 -Dsun.rmi.dgc.server.gcInterval=3600000 ...
この例では、明示的なガベージ・コレクションの実行間隔をデフォルトの1回/分ではなく、1回/時間に指定します。ただし、これでも一部のオブジェクトが回収されるまでに長時間かかる場合があります。DGCのタイムリな処理に上限を設定する必要なければ、これらのプロパティの値をLong.MAX_VALUE
まで引き上げ、明示的なコレクションの時間間隔を事実上無限大にすることができます。
サーバー仮想マシンでは、ソフト参照がクライアント仮想マシンよりも長くライブに保たれます。クリア間隔を制御するには、コマンド行オプション-XX:SoftRefLRUPolicyMSPerMB=
<N>
を使用して、ソフト参照を(強到達可能ではなくなってから)ライブに保つ期間を、ヒープ内の空き領域1MB当たりのミリ秒(ms)で指定します。デフォルト値は1000ms/MBで、これは、(オブジェクトへの最後の強参照が収集された後の)ソフト参照がヒープ内の空き領域1MB当たり1秒の寿命であることを意味します。ソフト参照は、散発的に発生するガベージ・コレクション時にのみクリアされるので、これは概算値です。
JavaクラスにはJava Hotspot VM内の内部表現があり、それはクラス・メタデータと呼ばれます。以前のリリースのJava Hotspot VMで、クラス・メタデータはPermanentと呼ばれる永続的世代に割り当てられていました。JDK 8では、永続的(Permanent)世代が削除され、クラス・メタデータがネイティブ・メモリーに割り当てられます。クラス・メタデータに使用できるネイティブ・メモリーの容量はデフォルトで無制限です。クラス・メタデータに使用するネイティブ・メモリーの容量に上限を設定するには、オプションMaxMetaspaceSize
を使用します。
Java Hotspot VMではメタデータ用の領域を明示的に管理します。OSから領域が要求され、その領域がチャンクに分割されます。そのチャンクからクラス・ローダーがメタデータに領域を割り当てます(チャンクは特定のクラス・ローダーにバインドされます)。クラス・ローダーのクラスがアンロードされると、そのチャンクは再利用に回されるか、OSに返されます。メタデータは、malloc
ではなくmmap
によって割り当てられた領域を使用します。
UseCompressedOops
が有効でUseCompressedClassesPointers
が使用されている場合、クラス・メタデータには、論理的に異なる2つの領域のネイティブ・メモリーが使用されます。UseCompressedClassPointers
では、UseCompressedOops
のJavaオブジェクト参照と同様に、32ビット・オフセットを使用してクラス・ポインタを64ビット・プロセスで表現します。このような圧縮クラス・ポインタにリージョンが割り当てられます(32ビット・オフセット)。リージョンのサイズはCompressedClassSpaceSize
で設定でき、デフォルトは1ギガバイト(GB)です。圧縮クラス・ポインタの領域は、初期化時にmmap
によって割り当てられた領域として確保され、必要に応じてコミットされます。MaxMetaspaceSize
は、コミットされた圧縮クラスの領域と他のクラス・メタデータの領域の合計に適用されます。
クラス・メタデータは対応するJavaクラスのアンロード時に割当て解除されます。Javaクラスはガベージ・コレクションに伴ってアンロードされるので、クラスのアンロードとクラス・メタデータの割当て解除を行うためにガベージ・コレクションが誘発される場合があります。クラス・メタデータ用にコミットされた領域が一定のレベル(最高水位標)に到達すると、ガベージ・コレクションが発生します。ガベージ・コレクション後、クラス・メタデータから解放された領域量に応じて、最高水位標が上げられたり、下げられたりすることがあります。別のガベージ・コレクションがすぐに発生しないように、最高水位標は上げられます。最高水位標の初期値は、コマンド行オプションMetaspaceSize
の値に設定されます。MaxMetaspaceFreeRatio
オプションとMinMetaspaceFreeRatio
オプションを基に、これを上げ/下げします。クラス・メタデータで使用可能なコミットされた領域(クラス・メタデータ用にコミットされた総領域の割合)がMaxMetaspaceFreeRatio
より大きいと、最高水位標は下げられます。これがMinMetaspaceFreeRatio
より低いと、最高水位標は上げられます。
クラス・メタデータによるガベージ・コレクションの早期誘発を避けるには、オプションMetaspaceSize
に高めの値を指定します。アプリケーションに割り当てられるクラス・メタデータの容量はアプリケーションに依存するので、MetaspaceSize
の選択に一般的なガイドラインはありません。MetaspaceSize
のデフォルト・サイズは、12MBから約20MBの範囲でプラットフォームに依存します。
メタデータに使用される領域に関する情報はヒープの出力に含まれます。典型的な出力を例11-1「典型的なヒープ出力」に示します。
Heap PSYoungGen total 10752K, used 4419K [0xffffffff6ac00000, 0xffffffff6b800000, 0xffffffff6b800000) eden space 9216K, 47% used [0xffffffff6ac00000,0xffffffff6b050d68,0xffffffff6b500000) from space 1536K, 0% used [0xffffffff6b680000,0xffffffff6b680000,0xffffffff6b800000) to space 1536K, 0% used [0xffffffff6b500000,0xffffffff6b500000,0xffffffff6b680000) ParOldGen total 20480K, used 20011K [0xffffffff69800000, 0xffffffff6ac00000, 0xffffffff6ac00000) object space 20480K, 97% used [0xffffffff69800000,0xffffffff6ab8add8,0xffffffff6ac00000) Metaspace used 2425K, capacity 4498K, committed 4864K, reserved 1056768K class space used 262K, capacity 386K, committed 512K, reserved 1048576K
「Metaspace
」で始まる行のused
値は、ロード済クラスが使用している領域量です。capacity
値は、現在割り当てられているチャンク内のメタデータで使用可能な領域です。committed
値は、チャンクの使用可能な領域量です。reserved
値は、メタデータ用に確保された(必ずしもコミットされているとはかぎらない)領域量です。「class space
」で始まる行には、圧縮クラス・ポインタのメタデータの対応する値が含まれています。