コンカレント・マーク・スイープ(CMS)コレクタは、ガベージ・コレクションによる一時停止の短縮が優先され、アプリケーションの実行中にガベージ・コレクタとプロセッサ・リソースを共有する余裕があるアプリケーション向けに設計されています。一般に、データが比較的大量で寿命が長く(Tenured世代が大きい)、プロセッサを2個以上搭載したマシンで実行されるアプリケーションは、このコレクタを使用することによってメリットを得られるという傾向があります。ただし、一時停止時間を短くすることが要求されるすべてのアプリケーションで、このコレクタの使用を検討することをお薦めします。CMSコレクタを有効にするには、コマンド行オプション-XX:+UseConcMarkSweepGC
を使用します。
他の使用可能なコレクタと同様に、CMSコレクタは世代別です。つまり、マイナー・コレクションとメジャー・コレクションの両方が発生します。CMSコレクタは、別々のガベージ・コレクタ・スレッドを使用することでメジャー・コレクションによる一時停止時間を短縮し、アプリケーション・スレッドの実行と並行して到達可能なオブジェクトをトレースします。各メジャー・コレクション・サイクルにおいて、CMSコレクタはコレクションの開始時と半ば近くに、すべてのアプリケーション・スレッドを短期間停止します。2番目の一時停止は、この2回の一時停止のうちで長くなる傾向があります。どちらの一時停止でも、マルチスレッドを使用してコレクション作業が行われます。残りのコレクション作業(ライブ・オブジェクトのトレースと到達不可能なオブジェクトのスイープの大部分を含む)は、アプリケーションと並行して動作している1つ以上のガベージ・コレクタ・スレッドで実行されます。マイナー・コレクションは実行中のメジャー・サイクルとのインターリーブが可能で、パラレル・コレクタと同様の方法で実行されます(具体的には、マイナー・コレクション中にアプリケーション・スレッドが停止されます)。
CMSコレクタは、アプリケーション・スレッドと同時実行の1つ以上のガベージ・コレクタ・スレッドを使用して、Tenured世代がいっぱいになる前に、この世代のコレクションを完了しようとします。前に説明したように、CMSコレクタの通常の操作では、トレース作業とスイープ作業のほとんどをアプリケーション・スレッドの実行中に行うため、アプリケーション・スレッドの一時停止は非常に短くなります。ただし、Tenured世代がいっぱいになる前に到達不可能なオブジェクトの回収をCMSコレクタで完了できなかったり、Tenured世代内の使用可能な空き領域ブロックでは割当てを満たすことができない場合は、アプリケーションを一時停止して、すべてのアプリケーション・スレッドが停止した状態でコレクションを完了させることになります。コレクションを並行して完了できないことを並行モードの失敗と呼び、CMSコレクタのパラメータを調整する必要があることを示唆します。明示的なガベージ・コレクション(System.gc()
)によって、またはガベージ・コレクションが診断ツールに情報を提供する必要がある場合に、コンカレント・コレクションが中断されると、並行モードの中断が報告されます。
ガベージ・コレクションに時間がかかりすぎると、CMSコレクタはOutOfMemoryError
をスローします。具体的には、ガベージ・コレクションに合計時間の98%より多く費やされ、ヒープのリカバリが2%未満である場合に、OutOfMemoryError
がスローされます。この機能は、ヒープが小さすぎるためにアプリケーションが長時間わたって処理が進まないまま実行中の状態に陥ることを回避する目的で設計されています。必要な場合は、コマンド行に-XX:-UseGCOverheadLimit
オプションを追加することで、この機能を無効にできます。
このポリシーはパラレル・コレクタと同じですが、コンカレント・コレクションの実行に費やされた時間は98%の時間制限に加算されないという点が異なります。つまり、過剰なGC時間として考慮されるのは、アプリケーションの停止中に実行されたコレクションのみです。このようなコレクションは、並行モードの失敗または明示的コレクションのリクエスト(System.gc
の呼出しなど)に伴うのが一般的です。
CMSコレクタは、Java HotSpot VMに含まれる他のすべてのコレクタと同様にトレース・コレクタであり、少なくともヒープ内で到達可能なすべてのオブジェクトを識別します。Richard Jones氏とRafael D. Lins氏の著書『Garbage Collection: Algorithms for Automated Dynamic Memory』によると、これはインクリメンタル更新コレクタです。アプリケーション・スレッドとガベージ・コレクタ・スレッドはメジャー・コレクション中に並行して実行されるため、ガベージ・コレクタ・スレッドによってトレースされたオブジェクトが、後でコレクション・プロセスが終了するまでに、到達不可能になる場合があります。このように到達不可能で、まだ回収されていないオブジェクトは、フローティング・ガベージと呼ばれます。フローティング・ガベージの総量は、コンカレント・コレクション・サイクルの期間とアプリケーションによる参照更新の頻度(ミューテーションとも呼ばれる)に左右されます。さらに、若い世代とTenured世代は別々に収集されるので、相互に他方のルート・ソースとして機能します。大まかな目安として、フローティング・ガベージに備え、Tenured世代のサイズを20%拡大してみてください。あるコンカレント・コレクション・サイクルの終了時にヒープ内にあったフローティング・ガベージは、次のコレクション・サイクル時に収集されます。
CMSコレクタは、コンカレント・コレクション・サイクル時にアプリケーションを2回一時停止します。最初の一時停止は、ルートから直接到達可能なオブジェクト(アプリケーション・スレッドのスタックやレジスタからのオブジェクト参照、静的オブジェクトなど)と、ヒープ内のどこか(若い世代など)から到達可能なオブジェクトに、ライブのマークを付けるために行われます。最初の一時停止は、初期マークの一時停止と呼ばれます。2番目の一時停止はコンカレント・トレース・フェーズの最後に発生し、CMSコレクタがオブジェクトのトレースを終了した後にアプリケーション・スレッドがそのオブジェクト内の参照を更新したことが原因でコンカレント・トレース・フェーズ中に見つけることができなかったオブジェクトを検出します。この2番目の一時停止は、再マークの一時停止と呼ばれます。
初期マークの一時停止と再マークの一時停止の間に、到達可能なオブジェクト・グラフのコンカレント・トレースが発生します。このコンカレント・トレース・フェーズ中は、本来ならばアプリケーションに使用できたはずのプロセッサ・リソースを、1つ以上のコンカレント・ガベージ・コレクタ・スレッドが使用している場合があります。その結果、CPUバウンドなアプリケーションは、このようなコンカレント・フェーズ中に、アプリケーション・スレッドが一時停止されていない場合でも、アプリケーションのスループットが相応に低下する場合があります。再マークの一時停止後のコンカレント・スイープ・フェーズでは、到達不可能と識別されたオブジェクトを収集します。CMSコレクタは、コレクション・サイクルが完了すると、次のメジャー・コレクション・サイクルが開始するまで待機しますが、その間は計算のリソースをほとんど消費しません。
シリアル・コレクタでは、Tenured世代がいっぱいになるたびにメジャー・コレクションが発生し、コレクションの実行中はすべてのアプリケーション・スレッドが停止します。これに対し、コンカレント・コレクションでは、Tenured世代がいっぱいになる前にコレクションを完了できるよう、開始のタイミングを計る必要があります。そうしないと、並行モードの失敗に伴いアプリケーションの一時停止が長くなるおそれがあります。コンカレント・コレクションを開始するには、複数の方法があります。
CMSコレクタでは、最新の履歴に基づいて、Tenured世代が枯渇するまでの残り時間とコンカレント・コレクション・サイクルに必要な時間を予測しています。これらの動的な予測を使用して、Tenured世代が枯渇する前にコンカレント・サイクルが完了するよう、コンカレント・コレクション・サイクルが開始されます。並行モードの失敗はコストが非常に高くなる可能性があるので、このような予測は安全のための措置です。
Tenured世代の占有率が開始占有率(Tenured世代の割合)を超えた場合にも、コンカレント・コレクションが開始されます。この開始占有率のデフォルトのしきい値は約92%ですが、この値はリリースごとに変更される場合があります。この値は、コマンド行オプション-XX:CMSInitiatingOccupancyFraction=
<N>
(<N>
は、Tenured世代のサイズの割合を0から100の整数で表した値)を使用して、手動で調整できます。
若い世代のコレクションとTenured世代のコレクションの一時停止は別々に行われます。両方が重なることはありませんが、間断なく発生する場合があり、あるコレクションによる一時停止の直後に別のコレクションによる停止が続くと、それが1つの長い一時停止に見えることがあります。これを回避するため、CMSコレクタでは前回と次回の若い世代の一時停止のほぼ中間に、再マークの一時停止をスケジュールしようとします。初期マークの一時停止(再マークの一時停止よりも通常は大幅に短い)には、このスケジューリングは現在行われていません。
インクリメンタル・モードはJava SE 8で非推奨となり、今後のメジャー・リリースでは削除される可能性があります。
CMSコレクタは、コンカレント・フェーズがインクリメンタルに行われるモードで使用できます。すでに説明したように、コンカレント・フェーズ中はガベージ・コレクタ・スレッドが1つ以上のプロセッサを使用しています。インクリメンタル・モードは、コンカレント・フェーズを定期的に停止してプロセッサをアプリケーションに返すことにより、長時間にわたるコンカレント・フェーズの影響を軽減することを目的としています。このモード(ここではi-cmsと呼ばれる)は、コレクタによって並行実行される作業を小さな時間のチャンクに分割して、若い世代のコレクションとコレクションの間にスケジュールします。この機能は、CMSコレクタによる一時停止を短くする必要があるアプリケーションを、搭載プロセッサ数が少ない(1、2個程度)マシン上で実行している場合に役立ちます。
コンカレント・コレクション・サイクルの一般的な手順は次のとおりです。
すべてのアプリケーション・スレッドを停止し、ルートから到達可能な一連のオブジェクトを識別した後で、すべてのアプリケーション・スレッドを再開します。
アプリケーション・スレッドの実行中に、1つ以上のプロセッサを使用して、到達可能なオブジェクト・グラフを並行してトレースします。
前の手順のトレース以降に変更されたオブジェクト・グラフのセクションを、1つのプロセッサを使用して並行して再トレースします。
すべてのアプリケーション・スレッドを停止し、前回の調査から変更されている可能性のあるルートのセクションとオブジェクト・グラフを再トレースした後で、すべてのアプリケーション・スレッドを再開します。
1つのプロセッサを使用して、到達不可能なオブジェクトを割当て用空きリストに並行してスイープします。
1つのプロセッサを使用して、並行してヒープ・サイズを変更し、次のコレクション・サイクルのサポート・データ構造を準備します。
通常、CMSコレクタは、コンカレント・トレース・フェーズ全体を通して1つ以上のプロセッサを使用し、それを自発的に放棄することはありません。同様に、コンカレント・スイープ・フェーズ全体を通して1つのプロセッサが使用され、これも放棄されることはありません。本来ならば処理コアを使用していたはずのアプリケーションにレスポンス時間の制約があり、特にプロセッサを1、2個しか搭載していないシステム上で実行されている場合は、このオーバーヘッドがアプリケーションの長期間の中断につながる可能性があります。インクリメンタル・モードでは、コンカレント・フェーズを短いアクティビティのバーストに分割し、それをマイナー・コレクションの一時停止と一時停止の中間にスケジュールすることで、この問題を解決します。
i-cmsモードでは、デューティ・サイクルを使用して、CMSコレクタがプロセッサを自発的に放棄する前に実行することが許可されている作業量を制御します。デューティ・サイクルは、CMSコレクタが実行を許可されている若い世代のコレクション間の時間の割合です。i-cmsモードでは、アプリケーションの動作に基づいてデューティ・サイクルを自動的に計算するか(自動調整と呼ばれ、これが推奨方法です)、コマンド行でデューティ・サイクルを固定値に設定できます。
表8-1「i-cmsのコマンド行オプション」に、i-cmsモードを制御するコマンド行オプションを一覧で示します。「推奨オプション」の項に、推奨されるオプションの初期設定を示します。
表8-1 i-cmsのコマンド行オプション
オプション | 説明 | デフォルト値(Java SE 5以前) | デフォルト値(Java SE 6以降) |
---|---|---|---|
|
インクリメンタル・モードを有効にします。このオプションが機能するためには、CMSコレクタも有効にする必要があります( |
無効 |
無効 |
|
自動調整を有効にします。JVMの実行中に収集された統計に基づいて、インクリメンタル・モードのデューティ・サイクルが自動的に調整されます。 |
無効 |
無効 |
|
CMSコレクタが実行を許可されているマイナー・コレクション間の時間の割合(0から100まで)です。 |
50 |
10 |
|
|
10 |
0 |
|
デューティ・サイクルの計算時に、保守性を追加するために使用される割合(0から100)です。 |
10 |
10 |
|
インクリメンタル・モードのデューティ・サイクルをマイナー・コレクション間で期間内に右に移動する割合(0から100まで)です。 |
0 |
0 |
|
CMSコレクションの統計のための指数平均の計算時に、現在のサンプルの重み付けに使用される割合(0から100)です。 |
25 |
25 |
Java SE 8でi-cmsを使用するには、次のコマンド行オプションを使用します。
-XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode \ -XX:+PrintGCDetails -XX:+PrintGCTimeStamps
最初の2つのオプションは、CMSコレクタとi-cmsをそれぞれ有効にします。最後の2つのオプションは必須ではありません。ガベージ・コレクションに関する診断情報を標準出力に書き出すだけですが、これでガベージ・コレクションの動作を確認して後で分析できます。
Java SE 5以前のリリースでは、次をi-cmsのコマンド行オプションの初期設定として使用することをお薦めします。
-XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode \ -XX:+PrintGCDetails -XX:+PrintGCTimeStamps \ -XX:+CMSIncrementalPacing -XX:CMSIncrementalDutyCycleMin=0 -XX:CMSIncrementalDutyCycle=10
JavaSE8にも同じ値が推奨されますが、i-cmsの自動調整を制御する3つのオプションの値はJavaSE6でデフォルトになりました。
i-cmsの自動調整機能では、プログラムの実行中に収集された統計を使用して、ヒープがいっぱいになる前にコンカレント・コレクションが完了するよう、デューティ・サイクルを計算します。ただし、過去の動作から未来の動作を完全に予測することはできないので、ヒープがいっぱいになることを必ず回避できるほど予測が正確とはかぎりません。フル・コレクションが頻発する場合は、表8-2「i-cmsの自動調整機能のトラブルシューティング」の手順を1つずつ実行してください。
例8-1「CMSコレクタの出力」は、-verbose:gc
オプションと-XX:+PrintGCDetails
オプションを指定した場合のCMSコレクタからの出力です(あまり重要でない細部は省略してあります)。CMSコレクタの出力中にマイナー・コレクションからの出力が点在していることに注意してください。通常、コンカレント・コレクション・サイクル中にマイナー・コレクションが何回も発生します。「CMS-initial-mark」はコンカレント・コレクション・サイクルの開始を示し、「CMS-concurrent-mark」はコンカレント・マーキング・フェーズの終了を示し、「CMS-concurrent-sweep」はコンカレント・スイープ・フェーズの終了をマークします。「CMS-concurrent-preclean」で示されたプレクリーニング・フェーズについては、まだ説明していません。プレクリーニングは、再マーク・フェーズ(CMS-remark)の準備として並行実行できる作業を表します。最終フェーズは「CMS-concurrent-reset」で示され、次回のコンカレント・コレクションの準備として行われます。
例8-1 CMSコレクタの出力
[GC [1 CMS-initial-mark: 13991K(20288K)] 14103K(22400K), 0.0023781 secs] [GC [DefNew: 2112K->64K(2112K), 0.0837052 secs] 16103K->15476K(22400K), 0.0838519 secs] ... [GC [DefNew: 2077K->63K(2112K), 0.0126205 secs] 17552K->15855K(22400K), 0.0127482 secs] [CMS-concurrent-mark: 0.267/0.374 secs] [GC [DefNew: 2111K->64K(2112K), 0.0190851 secs] 17903K->16154K(22400K), 0.0191903 secs] [CMS-concurrent-preclean: 0.044/0.064 secs] [GC [1 CMS-remark: 16090K(20288K)] 17242K(22400K), 0.0210460 secs] [GC [DefNew: 2112K->63K(2112K), 0.0716116 secs] 18177K->17382K(22400K), 0.0718204 secs] [GC [DefNew: 2111K->63K(2112K), 0.0830392 secs] 19363K->18757K(22400K), 0.0832943 secs] ... [GC [DefNew: 2111K->0K(2112K), 0.0035190 secs] 17527K->15479K(22400K), 0.0036052 secs] [CMS-concurrent-sweep: 0.291/0.662 secs] [GC [DefNew: 2048K->0K(2112K), 0.0013347 secs] 17527K->15479K(27912K), 0.0014231 secs] [CMS-concurrent-reset: 0.016/0.016 secs] [GC [DefNew: 2048K->1K(2112K), 0.0013936 secs] 17527K->15479K(27912K), 0.0014814 secs ]
通常、初期マークの一時停止はマイナー・コレクションの一時停止時間よりも短くなります。コンカレント・フェーズ(コンカレント・マーク、コンカレント・プレクリーニングおよびコンカレント・スイープ)は、マイナー・コレクションの一時停止よりも大幅に長くなるのが一般的です(例8-1「CMSコレクタの出力」を参照)。ただし、このようなコンカレント・フェーズ中にアプリケーションが一時停止されることはありません。通常、再マークの一時停止はマイナー・コレクションと同程度の長さです。再マークの一時停止が影響を受けるのは、特定のアプリケーション特性(たとえば、オブジェクト変更の頻度が高いと、この一時停止が長くなる可能性があります)と、前回のマイナー・コレクションからの経過時間(たとえば、若い世代のオブジェクトが増えると、この一時停止が長くなる可能性があります)によります。