3つのGCを使い分けてCPUの使用率をコントロールする
- 「GCの時間が長くてシステムが反応してくれない……もっと短くならないかな?」
- 「GCが始まると、CPUが占有されちゃって、ほかのプロセスの動きが重くなるんだよね……」
- 「GCの停止時間が多少長くなってもいいから、ほかのプロセスへの影響を軽くできないかな?」
JVMを使用しているシステムでは、そんな話を耳にします。GCが起きていることまでは把握できているのですが、それからどうしたら良いのかわからないのです。
GCは、JVMの内部でGCスレッドがCPUに処理されることで行われています。そのため、GCが行われている間はCPUの使用率が増加してしまうのです。
GCをスレッドの観点で見ると、以下の3種類があります。
これらの違いを把握すれば、CPUの使用率とアプリケーションの停止時間をコントロールするヒントが見えてきます。
では、3つのGCにはそれぞれどのような特徴があるのでしょうか。1つずつ見ていきましょう。
最もシンプルな「シリアルGC」
3つの中で最もシンプルなのがシリアルGCです。シリアルGCでは、すべてのアプリケーションスレッドを停止し、1つのスレッドでGCを実行します。
シリアルGCでは、GCを1つのスレッドで行っているため、1つのコアしか使われません。たくさんのコアが搭載されている環境では、ほかのコアは 何も処理を行いません。
最近の環境では、JVMに指定するヒープサイズが大きくなっています。ヒープサイズが大きくなると、アプリケーションはたくさんのオブジェクトをヒープ内に格納できるようになります。その一方で、GCの対象となる領域が大きくなり、GCの対象となるオブジェクトの数も増えるため、GCによるアプリケーションの停止時間が長くなってしまうのです。
GCによるアプリケーションの停止時間が長くなってしまうと、アプリケーションの処理時間が短くなってしまいます。
この問題を解決する1つの方法として、アプリの改修によって、オブジェクトの生成量を少なくして、GCの発生自体を減らす方法が考えられます。
しかし、アプリを改修すると、設計・開発だけではなく、さまざまなテストを行う必要があり、非常にコストがかかってしまいます。そのため、アプリの改修はコストとメリットを検討して対応しなければなりません。
複数スレッドで実行して停止時間を短縮する「パラレルGC」
では、GCによる停止時間をできるだけ短くするにはどうすれば良いでしょうか?
その手段の1つが、パラレルGCです。パラレルGCでは、シリアルGCと同じくすべてのアプリケーションスレッドを停止しながらも、複数のスレッドでGCを実行することで、アプリケーションの停止時間を短縮することができます。
シリアルGCにくらべて停止時間が短くなるため、アプリケーションのレスポンスタイムを短くすることができ、スループットの向上も見込めます。
しかし、アプリケーションスレッドの停止時間は、厳密には
までは短縮されません。それは、シリアルGCに比べ、GCスレッド間の同期などにオーバーヘッドがかかり、GCスレッドの処理時間が増加するためです。
つまり、パラレルGCを使用しても、少なからずアプリケーションスレッドの停止時間は発生してしまいます。
アプリケーションと同時にGCを動作させる「コンカレントGC」
アプリケーションを停止せずにGCを実行することはできるのでしょうか?
そこで登場するのが、「コンカレントGC」です。コンカレントGCでは、アプリケーションスレッドと同時にGCスレッドを動作させることで、アプリケーションの停止時間なくGCを行うことができます。
パラレルGCと同様、シリアルGCよりもオーバーヘッドがかかるため、GCスレッドの総実行時間は増加しますが、アプリケーションと同時に実行するため、停止時間は短縮します。
一見、コンカレントGCを使うことで、停止時間の存在しないシステムを構築できそうに思えるかもしれません。しかし、実際はそうはいきません。
GCは、実装によってさまざまなフェーズがあり、コンカレントに処理を行えるフェーズと、そうではないフェーズが存在します。すべてのフェーズがコンカレントで処理することができないため、停止時間を完全にゼロにすることはできないのです。
次回は、GCにはどのようなアルゴリズムが使われているのかをご紹介します。お楽しみに。