1. 序章

JVMには、さまざまなデプロイメントオプションをサポートするためのガベージコレクションのさまざまなオプションが付属しています。 これにより、アプリケーションに使用するガベージコレクターを柔軟に選択できます。

デフォルトでは、JVMはホストコンピュータのクラスに基づいて最も適切なガベージコレクタを選択します。 ただし、場合によっては、アプリケーションでGC関連の重大なボトルネックが発生し、使用するアルゴリズムをより細かく制御する必要があります。 問題は、「GCアルゴリズムをどのように決定するのか」です。

この記事では、その質問に答えようとします。

2. GCとは何ですか?

Javaはガベージコレクション言語であるため、アプリケーションへのメモリの手動割り当てと割り当て解除の負担から保護されています。 OSによってJVMプロセスに割り当てられたメモリのチャンク全体は、ヒープと呼ばれます。 次に、JVMは、このヒープを世代と呼ばれる2つのグループに分割します。 この内訳により、効率的なメモリ管理のためにさまざまな手法を適用できます。

young(Eden)世代は、新しく作成されたオブジェクトが割り当てられる場所です。 通常は小さく(100-500MB)、2つのサバイバースペースもあります。 古い世代は、古いオブジェクトまたは古いオブジェクトが保存される場所です —これらは通常長寿命のオブジェクトです。 このスペースは若い世代よりもはるかに大きいです。

コレクターは継続的に若い世代の満腹を追跡し、生きているオブジェクトがサバイバースペースの1つに移動され、死んだオブジェクトが削除されるマイナーコレクションをトリガーします。 オブジェクトが特定の数のマイナーGCを生き残った場合、コレクターはそのオブジェクトを古い世代に移動します。 古いスペースがいっぱいであると見なされると、メジャーGCが発生し、古いスペースからデッドオブジェクトが削除されます。

これらの各GCの間、 stop-the-world フェーズがあり、その間は他に何も起こりません。アプリケーションは要求を処理できません。 これを一時停止時間と呼びます。

3. 考慮すべき変数

GCは手動のメモリ管理から私たちを保護するのと同じように、コストをかけてこれを実現します。 GCランタイムのオーバーヘッドを可能な限り低く抑えることを目指す必要があります。 アプリケーションのニーズに最適なコレクターを決定するのに役立つ変数がいくつかあります。 このセクションの残りの部分でそれらについて説明します。

3.1. ヒープサイズ

これは、OSによってJVMに割り当てられた作業メモリーの合計量です。 理論的には、メモリが大きいほど、収集前により多くのオブジェクトを保持できるため、GC時間が長くなります。 最小および最大ヒープサイズは、を使用して設定できます -Xms = -Xmx = コマンドラインオプション。

3.2. アプリケーションデータセットのサイズ

これは、アプリケーションが効果的に機能するためにメモリに保持する必要があるオブジェクトの合計サイズです。 すべての新しいオブジェクトは若い世代のスペースにロードされるため、これは間違いなく最大ヒープサイズに影響し、したがってGC時間に影響します。

3.3. CPUの数

これは、マシンが使用可能なコアの数です。 この変数は、選択するアルゴリズムに直接影響します。 複数のコアが使用可能な場合にのみ効率的なものもあり、他のアルゴリズムではその逆が当てはまります。

3.4. 一時停止時間

一時停止時間は、ガベージコレクターがアプリケーションを停止してメモリを再利用する期間です。 この変数はレイテンシーに直接影響するため、目標はこれらの一時停止の最長を制限することです。

3.5. スループット

これは、プロセスが実際にアプリケーション作業を行うために費やす時間を意味します。 に対して適用時間は長くなります。 GC作業のオーバーヘッド時間は、アプリケーションのスループットが高くなります。

3.6. メモリーフットプリント

このは、GCプロセスで使用される作業メモリーです。 セットアップのメモリが限られているか、プロセスが多い場合、この変数がスケーラビリティを決定する可能性があります。

3.7. 迅速さ

このは、オブジェクトが停止してから、オブジェクトが占有しているメモリが再利用されるまでの時間です。 ヒープサイズに関連しています。 理論的には、ヒープサイズが大きいほど、収集のトリガーに時間がかかるため、迅速性は低くなります。

3.8. Javaバージョン

新しいJavaバージョンが登場すると、通常、サポートされているGCアルゴリズムとデフォルトのコレクターに変更が加えられます。 デフォルトのコレクターとそのデフォルトの引数から始めることをお勧めします。 各引数を微調整すると、選択したコレクターに応じてさまざまな効果があります。

3.9. レイテンシー

これは、アプリケーションの応答性です。 GCの一時停止は、この変数に直接影響します。

4. ガベージコレクター

シリアルGCに加えて、他のすべてのコレクターは、複数のコアが使用可能な場合に最も効果的です。

4.1. シリアルGC

シリアルコレクターは、単一のスレッドを使用してすべてのガベージコレクション作業を実行します。 特定の小さなハードウェアおよびオペレーティングシステム構成ではデフォルトで選択されていますが、オプション -XX:+UseSerialGCを使用して明示的に有効にすることもできます。

長所:

  • スレッド間通信のオーバーヘッドがなければ、比較的効率的です。
  • クライアントクラスのマシンや組み込みシステムに適しています。
  • 小さなデータセットを持つアプリケーションに適しています。
  • マルチプロセッサハードウェアでも、データセットが小さい場合(最大100 MB)、それでも最も効率的です。

短所:

  • 大規模なデータセットを持つアプリケーションには効率的ではありません。
  • マルチプロセッサハードウェアを利用することはできません。

4.2. パラレル/スループットGC

このコレクターは、複数のスレッドを使用してガベージコレクションを高速化します。 Javaバージョン8以前では、サーバークラスのマシンのデフォルトです。 -XX:+ UseParallelGC オプションを使用すると、このデフォルトをオーバーライドできます。

長所:

  • マルチプロセッサハードウェアを利用できます。
  • シリアルGCよりも大きなデータセットの方が効率的です。
  • 全体的に高いスループットを提供します。
  • メモリーフットプリントを最小限に抑えようとします。

短所:

  • アプリケーションでは、stop-the-world操作中に長い一時停止時間が発生します。
  • ヒープサイズにうまく対応できません。

バッチタスク、オフラインジョブ、ウェブサーバーなどの非インタラクティブなアプリの場合のように、スループットを向上させ、一時停止時間を気にしない場合に最適です。

4.3. コンカレントマークスイープ(CMS)GC

CMSはほとんど同時コレクターであると考えています。 これは、アプリケーションと同時に高価な作業を実行することを意味します。 パラレルおよびシリアルコレクターのフルGCに関連する長い一時停止を排除することにより、低レイテンシー向けに設計されています。

オプション-XX:+ UseConcMarkSweepGC を使用して、CMSコレクターを有効にすることができます。 コアJavaチームは、Java 9で非推奨になり、Java14で完全に削除しました。

長所:

  • 一時停止時間を最小限に抑えるため、低遅延のアプリケーションに最適です。
  • ヒープサイズに応じて比較的適切にスケーリングされます。
  • マルチプロセッサマシンを利用できます。

短所:

  • Java 9で非推奨になり、Java14で削除されました。
  • データセットが巨大なサイズに達したとき、または巨大なヒープを収集するときは、比較的非効率になります。
  • 同時フェーズ中にアプリケーションがGCとリソースを共有する必要があります。
  • GC操作に全体的に多くの時間が費やされるため、スループットの問題が発生する可能性があります。
  • 全体として、ほとんどが同時性であるため、より多くのCPU時間を使用します。

4.4. G1(ガベージファースト)GC

G1は、CMSと同じように、複数のバックグラウンドGCスレッドを使用してヒープをスキャンしてクリアします。 実際、コアJavaチームはG1をCMSの改善として設計し、その弱点のいくつかに追加の戦略を適用しました。

増分および同時収集に加えて、以前のアプリケーションの動作とGCの一時停止を追跡して、予測可能性を実現します。 次に、最初に最も効率的な領域(ほとんどがゴミでいっぱいの領域)のスペースを再利用することに焦点を当てます。 このため、これをGarbage-Firstと呼びます。

Java 9以降、G1はサーバークラスのマシンのデフォルトのコレクターです。 コマンドラインで-XX:+ UseG1GC を指定することで、明示的に有効にできます。

長所:

  • 巨大なデータセットでは非常に効率的です。
  • マルチプロセッサマシンを最大限に活用します。
  • 一時停止時間の目標を達成するのに最も効率的です。

短所:

  • 厳密なスループット目標がある場合、これは最善ではありません。
  • 同時収集中にアプリケーションがGCとリソースを共有する必要があります。

G1は、トレーディングプラットフォームやインタラクティブなグラフィックプログラムなどのリアルタイムアプリケーションなど、非常に厳密な一時停止時間の目標と適度な全体的なスループットを備えたアプリケーションに最適です。

5. 結論

多くのアプリケーションでは、コレクターの選択が問題になることはありません。通常、JVMのデフォルトで十分です。 つまり、アプリケーションは、許容可能な頻度と期間の一時停止でガベージコレクションが存在する場合でも正常に実行できます。 ただし、これは、大規模なクラスのアプリケーション、特に巨大なデータセット、多くのスレッド、および高いトランザクション率を持つアプリケーションには当てはまりません。

この記事では、JVMでサポートされているガベージコレクターについて説明しました。 また、アプリケーションのニーズに合った適切なコレクターを選択するのに役立つ主要な変数についても見てきました。