HotSpot JVMを使うとき,どのようにGCを設定していますか?
検索して出てきたホームページを見て
「とりあえずこのホームページにあるように最新のGCを指定したし,同じようにオプションを設定したから大丈夫だろ」
と思ってしまう話をよく見聞きします。
図1 誤ったGCの選択

しかし,たとえばバッチのようにスループットを意識すべき処理にもかかわらず,レスポンスタイム重視のGCを選んでしまうのは適切ではありません。
最終回となる今回は,HotSpotにはどのようなGCがあり,どのようにそれらを選択すれば良いのかを紹介します。
4つのGCを使いこなす
HotSpot JVMには,以下の4つのGCが実装されています。
- シリアルGC
- パラレルGC
- コンカレント マーク&スイープGC(CMS)
- ガベージファーストGC(G1GC)
1つずつ見ていきましょう。
シリアルGC
このGCの特徴は,ヒープの空き領域が少なくなるとシングルスレッドでマーク&スイープとコンパクションを行うことです。
昔はデフォルトでしたが,マルチコア化が進み,かつヒープサイズが大きくなった今では,GCに時間がかかり,停止時間が長くなってしまうため,積極的に使うことは少ないでしょう(現在も,シングルコア環境ではデフォルトです)。
図2 シリアルGC

パラレルGC
マルチコア環境ではこのGCがデフォルトです。
このGCの特徴は,ヒープの空き容量が少なくなると,マルチスレッドでマーク&スイープとコンパクションを行うことです。複数のスレッドでGCすることで,アプリケーションの停止時間がシリアルGCに比べて短くなり,アプリケーションを実行できる時間が長くなります。そのため,スループットが高くなるでしょう。
図3 パラレルGC

コンカレント マーク&スイープGC(CMS)
CMSの特徴は,アプリケーションと同時に実行できるフェーズと,アプリケーションを止めて実行するフェーズに分かれていることです。
これらのフェーズのうち,図4の実線部である①initialフェーズと③remarkフェーズのみがアプリケーションを停止させます。
②コンカレントマーク&プリクリーン,④コンカレントスイープ,⑤リセットはコンカレントに実行されるため,アプリケーションと同時に実行されます。
図4 CMS

CMSでは,GCで使用するスレッド同士の協調処理などにCPUリソースを使わなければならないため,アプリケーションのスループット低下が見込まれます。しかし,一部のフェーズではアプリケーションを停止する必要がないため,アプリケーション全体の停止時間は短くなります。その結果,GCがレスポンスタイムに与える影響が小さくなるでしょう。
ガベージファーストGC(G1GC)
最後にG1GCですが,これまでの3つのGCとは異なり,ヒープを「リージョン」と呼ばれる小さい領域に分割して使用するのが特徴です。CMSと同様,アプリケーションと同時に実行できるフェーズもあるため,結果としてレスポンスタイムの短縮が見込まれます。
どのようにGCを使い分けるか
GCの使い分けは,本連載の第3回で解説したように,スループットとレスポンスタイムを考慮します。
まずはじめは,レスポンスタイムの制約の有り無しにかかわらず,デフォルトのGCを選択しましょう。つまり,マルチコア環境ではパラレルGC,シングルコア環境ならシリアルGCになります。これらのGCはアプリケーションのスループットを重視するため,アプリケーションへCPUリソースを多く割り当てられます。
図5 スループット重視のGC

最近ではヒープのサイズが大きくなり,ヒープ内に作成できるオブジェクトの数が増えています。そのため,ヒープサイズに対し使用できるCPUリソースの割合がこれまでと変わらない場合はこれまで同様のGCが見込めますが,ヒープサイズに対して使用できるCPUリソースの割合が少なくなることが多いです。これにより,パラレルGCやシリアルGCでは停止時間が長期化し,レスポンスタイムの要件を満たせないことがあります。
図6 パラレルでは停止時間が長くなる

マルチコア環境において,パラレルGCではレスポンスタイムの要件が満たせない場合に,コンカレントであるCMSやG1GCを試してみましょう。
CMSやG1GCにするとスループットが低下する恐れがあるため,システム全体のスループット要件を満たせなくなるかもしれません。その場合は,スケールアップやスケールアウトによってリソースを追加するか,チューニングによって,レスポンスタイムとスループットの兼ね合いをとりましょう。
図7 レスポンスタイム重視のGC

まとめ
これまで,一般的なヒープやGCの話から,HotSpot JVMがどのような実装をされているかをご紹介していきました。
すべての処理に対して1つの正解が存在するわけではありません。
まずは,実行する処理の特徴を理解しましょう。そして,使用するJVMにはどのような実装がされているのかを把握してください。
それらを組み合わせて,処理に最適なGCを選択することが重要になります。