8 コンカレント・マーク・スイープ(CMS)コレクタ

コンカレント・マーク・スイープ(CMS)コレクタは、ガベージ・コレクションによる一時停止の短縮が優先され、アプリケーションの実行中にガベージ・コレクタとプロセッサ・リソースを共有する余裕があるアプリケーション向けに設計されています。

一般に、データが比較的大量で寿命が長く(古い世代が大きい)、プロセッサを2個以上搭載したマシンで実行されるアプリケーションは、このコレクタを使用することによってメリットを得られるという傾向があります。CMSコレクタを有効にするには、コマンド行オプション-XX:+UseConcMarkSweepGCを使用します。

CMSコレクタは、非推奨になりました。かわりにガベージファースト・コレクタの使用を検討してください。

コンカレント・マーク・スイープ・コレクタのパフォーマンスと構造

他の使用可能なコレクタと同様に、CMSコレクタは世代別です。つまり、マイナー・コレクションとメジャー・コレクションの両方が発生します。CMSコレクタは、別々のガベージ・コレクタ・スレッドを使用することでメジャー・コレクションによる一時停止時間を短縮し、アプリケーション・スレッドの実行と並行して到達可能なオブジェクトをトレースします。

各メジャー・コレクション・サイクルにおいて、CMSコレクタはコレクションの開始時と半ば近くに、すべてのアプリケーション・スレッドを短期間停止します。2番目の一時停止は、この2回の一時停止のうちで長くなる傾向があります。どちらの一時停止でも、マルチスレッドでコレクション作業が行われます。残りのコレクション作業(ライブ・オブジェクトのトレースと到達不可能なオブジェクトのスイープの大部分を含む)は1つ以上のガベージ・コレクタ・スレッドで実行されます。マイナー・コレクションは実行中のメジャー・サイクルとのインターリーブが可能で、パラレル・コレクタと同様の方法で実行されます(具体的には、マイナー・コレクション中にアプリケーション・スレッドが停止されます)。

並行モードの失敗

CMSコレクタは、アプリケーション・スレッドと同時実行の1つ以上のガベージ・コレクタ・スレッドを使用して、古い世代がいっぱいになる前に、この世代のコレクションを完了しようとします。

前に説明したように、CMSコレクタの通常の操作では、トレース作業とスイープ作業のほとんどをアプリケーション・スレッドの実行中に行うため、アプリケーション・スレッドの一時停止は非常に短くなります。ただし、古い世代がいっぱいになる前に到達不可能なオブジェクトの回収をCMSコレクタで完了できなかったり、古い世代内の使用可能な空き領域ブロックで割当てを満たすことができない場合は、アプリケーションを一時停止して、すべてのアプリケーション・スレッドが停止した状態でコレクションを完了させることになります。コレクションを並行して完了できないことを並行モードの失敗と呼び、CMSコレクタのパラメータを調整する必要があることを示唆します。明示的なガベージ・コレクション(System.gc())によって、またはガベージ・コレクションが診断ツールに情報を提供する必要がある場合に、コンカレント・コレクションが中断されると、並行モードの中断が報告されます。

過剰なGC時間とOutOfMemoryError

ガベージ・コレクションに時間がかかりすぎると、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』によると、これはインクリメンタル更新コレクタです。アプリケーション・スレッドとガベージ・コレクタ・スレッドはメジャー・コレクション中に並行して実行されるため、ガベージ・コレクタ・スレッドによってトレースされたオブジェクトが、後でコレクション・プロセスが終了するまでに、到達不可能になる場合があります。このように到達不可能で、まだ回収されていないオブジェクトは、ローティング・ガベージと呼ばれます。フローティング・ガベージの総量は、コンカレント・コレクション・サイクルの期間とアプリケーションによる参照更新の頻度(ミューテーションとも呼ばれる)に左右されます。さらに、若い世代と古い世代は別々に収集されるので、相互に他方のルート・ソースとして機能します。大まかな目安として、フローティング・ガベージに備え、古い世代のサイズを20%拡大してみてください。あるコンカレント・コレクション・サイクルの終了時にヒープ内にあったフローティング・ガベージは、次のコレクション・サイクル時に収集されます。

コンカレント・マーク・スイープ・コレクタの一時停止

CMSコレクタは、コンカレント・コレクション・サイクル時にアプリケーションを2回一時停止します。最初の一時停止は、ルートから直接到達可能なオブジェクト(アプリケーション・スレッドのスタックやレジスタからのオブジェクト参照、静的オブジェクトなど)と、ヒープ内のどこか(若い世代など)から到達可能なオブジェクトに、ライブのマークを付けるために行われます。

最初の一時停止は、初期マークの一時停止と呼ばれます。2番目の一時停止はコンカレント・トレース・フェーズの最後に発生し、CMSコレクタがオブジェクトのトレースを終了した後にアプリケーション・スレッドがそのオブジェクト内の参照を更新したことが原因でコンカレント・トレース・フェーズ中に見つけることができなかったオブジェクトを検出します。この2番目の一時停止は、再マークの一時停止と呼ばれます。

コンカレント・マーク・スイープ・コレクタのコンカレント・フェーズ

初期マークの一時停止と再マークの一時停止の間に、到達可能なオブジェクト・グラフのコンカレント・トレースが発生します。

このコンカレント・トレース・フェーズ中は、本来ならばアプリケーションに使用できたはずのプロセッサ・リソースを、1つ以上のコンカレント・ガベージ・コレクタ・スレッドが使用している場合があります。その結果、CPUバウンドなアプリケーションは、このようなコンカレント・フェーズ中に、アプリケーション・スレッドが一時停止されていない場合でも、アプリケーションのスループットが相応に低下する場合があります。再マークの一時停止後のコンカレント・スイープ・フェーズでは、到達不可能と識別されたオブジェクトを収集します。CMSコレクタは、コレクション・サイクルが完了すると、次のメジャー・コレクション・サイクルが開始するまで待機しますが、その間は計算のリソースをほとんど消費しません。

コンカレント・コレクション・サイクルの開始

シリアル・コレクタでは、古い世代がいっぱいになるたびにメジャー・コレクションが発生し、コレクションの実行中はすべてのアプリケーション・スレッドが停止します。これに対し、CMSコレクタのコンカレント・コレクションでは、古い世代がいっぱいになる前にコレクションを完了できるよう、開始のタイミングを計る必要があります。そうしないと、並行モードの失敗に伴いアプリケーションの一時停止が長くなるおそれがあります。コンカレント・コレクションを開始するには、複数の方法があります。

CMSコレクタでは、最新の履歴に基づいて、古い世代が枯渇するまでの残り時間とコンカレント・コレクション・サイクルに必要な時間を予測しています。これらの動的な予測を使用して、古い世代が枯渇する前にコレクション・サイクルが完了するよう、コンカレント・コレクション・サイクルが開始されます。並行モードの失敗はコストが非常に高くなる可能性があるので、このような予測は安全のための措置です。

古い世代の占有率が開始占有率(古い世代の割合)を超えた場合にも、コンカレント・コレクションが開始されます。この開始占有率のデフォルトのしきい値は約92%ですが、この値はリリースごとに変更される場合があります。この値は、コマンド行オプション-XX:CMSInitiatingOccupancyFraction=<N> (<N>は、古い世代のサイズの割合を0から100の整数で表した値)を使用して、手動で調整できます。

一時停止のスケジューリング

若い世代のコレクションと古い世代のコレクションの一時停止は別々に行われます。

両方が重なることはありませんが、間断なく発生する場合があり、あるコレクションによる一時停止の直後に別のコレクションによる停止が続くと、それが1つの長い一時停止に見えることがあります。これを回避するため、CMSコレクタでは前回と次回の若い世代の一時停止のほぼ中間に、再マークの一時停止をスケジュールしようとします。初期マークの一時停止(再マークの一時停止よりも通常は大幅に短い)には、このスケジューリングは現在行われていません。

コンカレント・マーク・スイープ・コレクタの測定

次に、オプション-Xlog:gcを指定したCMSコレクタの出力を示します。

[121,834s][info][gc] GC(657) Pause Initial Mark 191M->191M(485M) (121,831s, 121,834s) 3,433ms
[121,835s][info][gc] GC(657) Concurrent Mark (121,835s)
[121,889s][info][gc] GC(657) Concurrent Mark (121,835s, 121,889s) 54,330ms
[121,889s][info][gc] GC(657) Concurrent Preclean (121,889s)
[121,892s][info][gc] GC(657) Concurrent Preclean (121,889s, 121,892s) 2,781ms
[121,892s][info][gc] GC(657) Concurrent Abortable Preclean (121,892s)
[121,949s][info][gc] GC(658) Pause Young (Allocation Failure) 324M->199M(485M) (121,929s, 121,949s) 19,705ms
[122,068s][info][gc] GC(659) Pause Young (Allocation Failure) 333M->200M(485M) (122,043s, 122,068s) 24,892ms
[122,075s][info][gc] GC(657) Concurrent Abortable Preclean (121,892s, 122,075s) 182,989ms
[122,087s][info][gc] GC(657) Pause Remark 209M->209M(485M) (122,076s, 122,087s) 11,373ms
[122,087s][info][gc] GC(657) Concurrent Sweep (122,087s)
[122,193s][info][gc] GC(660) Pause Young (Allocation Failure) 301M->165M(485M) (122,181s, 122,193s) 12,151ms
[122,254s][info][gc] GC(657) Concurrent Sweep (122,087s, 122,254s) 166,758ms
[122,254s][info][gc] GC(657) Concurrent Reset (122,254s)
[122,255s][info][gc] GC(657) Concurrent Reset (122,254s, 122,255s) 0,952ms
[122,297s][info][gc] GC(661) Pause Young (Allocation Failure) 259M->128M(485M) (122,291s, 122,297s) 5,797ms

ノート:

CMSコレクション(GC ID: 657)の出力中にマイナー・コレクション (GC ID: 658、659、660)からの出力が点在していることに注意してください。通常、コンカレント・コレクション・サイクル中にマイナー・コレクションが何回も発生します。一時停止の初期マークは、コンカレント・コレクション・サイクルの開始を示します。「Concurrent」で始まる行は、コンカレント・フェーズの開始と終了を示します。一時停止の再マークは最後の一時停止です。プレクリーニングフェーズは前述の例にありません。プレクリーニングは、再マーク・フェーズの準備として並行実行できる作業を表します。最終フェーズは「Concurrent Reset」で示され、次回のコンカレント・コレクションの準備として行われます。

通常、初期マークの一時停止はマイナー・コレクションの一時停止時間よりも短くなります。コンカレント・フェーズ(コンカレント・マーク、コンカレント・プレクリーニングおよびコンカレント・スイープ)は、マイナー・コレクションの一時停止よりも大幅に長くなるのが一般的です(CMSコレクタの出力例を参照)。ただし、このようなコンカレント・フェーズ中にアプリケーションが一時停止されることはありません。通常、再マークの一時停止はマイナー・コレクションと同程度の長さです。再マークの一時停止が影響を受けるのは、特定のアプリケーション特性(たとえば、オブジェクト変更の頻度が高いと、この一時停止が長くなる可能性があります)と、前回のマイナー・コレクションからの経過時間(たとえば、若い世代のオブジェクトが増えると、この一時停止が長くなる可能性があります)によります。