Javaパフォーマンス本を読んで #3

第5章 ガベージコレクションの基礎

ガベージコレクションの概要

Java言語の最も魅力的な特徴の1つに、開発者がオブジェクトのライフサイクルを管理しなくてもよいという点があります。ガベージコレクションとは基本的に、使われなくなったオブジェクトを探すこととそのオブジェクトに関連づけられたメモリを解放することで成り立っています。 メモリの断片化を防ぐために、不使用のメモリを解放して、メモリを領域を結合します。 簡単に言うと、ガベージコレクションのパフォーマンスは未使用のオブジェクトの発見とメモリの解放そしてヒープのコンパクト化という3つの要因によって決まります。

すべてのガベージコレクターは基本的に、オブジェクトを複数の領域に分類して管理し、それぞれのために別のヒープ領域を用意しています。これらの領域は、old(または、終身在職を意味するtenured)領域そしてyoung領域と呼ばれます。young領域はさらに、edenとsurvivorという2つの空間に分けられます。

利用するガベージコレクターの判断基準

どのガベージコレクターを利用するべきか判断する際には、全体としてパフォーマンスの目標を定めるとよいでしょう。

個々のリクエストに対するレスポンスタイムが重要な場合では、次のような点について検討が必要です。

  • スレッド停止によって、一部のリクエストが影響を受ける可能性があります。この影響を最小化するのが目標なら、コンカレント型ガベージコレクタを利用するのがよいでしょう。
  • 停止の影響を受けた外れ値よりも、レスポンスタイムの平均値が重要なら、スループット型ガベージコレクターの方がよい結果を得られるでしょう。
  • コンカレント型ガベージコレクターでは長い停止を避けられますが、その代償としてCPUの使用量は増加します。

バッチ処理型のアプリケーションでは、次のようなトレードオフを考慮しながらガベージコレクターを選択してください。

  • CPUの使用率が余裕があるなら、コンカレント型ガベージコレクターを使いましょう。フルガベージコレクションに伴う停止を回避でき、迅速に処理を終えることができます。
  • CPUリソースが限られている場合にコンカレント型ガベージコレクターを使うと、さらにCPUの使用量が増加してしまい、処理の完了までの時間はかえって延びるでしょう。

ガベージコレクションアルゴリズム

シリアル型ガベージコレクター

クライアントマシンのマシンでは、これがデフォルトのガベージコレクターになります。ヒープの処理を行うスレッドは1つです。マイナーガベージコレクションとフルガベージコレクションの両方で、アプリケーションスレッドをすべて停止した上で処理が行われます。

スループット型ガベージコレクター

サーバークラスのマシンでは、これがデフォルトのガベージコレクターになります。young領域の処理に複数のスレッドを利用します。したがって、マイナーガベージコレクションはシリアル型よりも大幅に高速です。また、old領域の処理に複数のスレッドを利用することもでき、Java 7u4以降ではこれがデフォルトの設定になっています。 ここでも、マイナーガベージコレクションとフルガベージコレクションの双方でアプリケーションスレッドはすべて停止します。

CMSガベージコレクター

スループット型やシリアル型のガベージコレクターで行われているような、フルガベージコレクションに伴う長い停止を回避するためにCMS(Concurrent Mark Sweep)ガベージコレクターは設計されました。フルガベージコレクションの際にアプリケーションスレッドを止めずにすむよう、CMSガベージコレクターでは1つまたは複数のスレッドを使ってバックグラウンドでold領域を探索し、使われていないオブジェクトを破棄します。バックグラウンドのスレッドによるold領域のスキャンが行われます。   アプリケーションスレッドと並行してバックグラウンドでヒープを検索するので、CPU使用率は増加します。

G1ガベージコレクター

大きなヒープ(およそい4GB以上)を最大限の停止時間で処理することを目標としています。アプリケーションスレッドの実行中にold領域の処理を並行して行えます。CMSを利用する場合よりも、フルガベージコレクションが必要になる可能性を低くできます。