Javaにおけるガベージコレクション
Javaのガベージコレクションは高度なトピックの1つです。JavaのGC知識は、アプリケーションの実行時パフォーマンスを微調整するのに役立ちます。
Javaにおけるガーベージコレクション
- In Java, the programmers don’t need to take care of destroying the objects that are out of use. The Garbage Collector takes care of it.
- Garbage Collector is a Daemon thread that keeps running in the background. Basically, it frees up the heap memory by destroying the unreachable objects.
- Unreachable objects are the ones that are no longer referenced by any part of the program.
- We can choose the garbage collector for our java program through JVM options, we will look into these in later section of this tutorial.
自動ゴミ収集はどのように機能するのでしょうか?
自動ガベージ収集は、ヒープメモリを調べ、到達不能なオブジェクト(「マーキング」とも呼ばれる)を特定し、圧縮して破壊するプロセスです。このアプローチの問題は、オブジェクトの数が増えるにつれて、ガベージ収集時間も増加し続けることです。なぜなら、到達不能なオブジェクトを探すために、オブジェクトのリスト全体を検索する必要があるからです。しかし、アプリケーションの経験的な分析によれば、ほとんどのオブジェクトは短命です。この動作を利用してJVMのパフォーマンスを向上させるために、一般的には「世代別ガベージ収集」と呼ばれる手法が採用されました。この方法では、ヒープスペースはYoung Generation、Old Generation(またはTenured Generation)、Permanent Generationのように世代に分割されます。Young Generationヒープスペースは、すべての新しいオブジェクトが作成される場所です。一度満杯になると、マイナーガベージ収集(またはマイナーGC)が行われます。これは、この世代のどのオブジェクトが死んでいるかを見ると、このプロセスは迅速です。Young Generationで生き残ったオブジェクトは年をとり、やがて古い世代に移動します。Old Generationは、長期にわたって生き残るオブジェクトを保存するために使用されます。通常、Young Generationオブジェクトに対して閾値が設定され、その年齢に達するとオブジェクトがOld Generationに移動します。最終的には、Old Generationも収集する必要があります。このイベントはメジャーガベージ収集(Major GC)と呼ばれます。通常、これは全ての生きているオブジェクトを対象にするため、はるかに遅くなります。また、Young GenerationとOld Generationのスペースの両方をクリーニングするフルGCもあります。最後に、Java 8ではJava 7までPermanent Generation(またはPerm Gen)が存在しました。これはアプリケーションで使用されるクラスとメソッドを説明するためにJVMが必要とするメタデータを含んでいましたが、Java 8で削除されました。
Javaのガベージコレクター
実際には、JVMは4つの異なるガベージコレクターを提供していますが、すべてが世代別です。それぞれに独自の利点と欠点があります。どのガベージコレクターを使用するかの選択は私たちに委ねられており、スループットやアプリケーションの停止時間には劇的な違いがあります。これにより、管理されたヒープは異なるセグメントに分割され、古くからの仮定に基づいてヒープ内のほとんどのオブジェクトは短命であり、素早く再利用されるべきです。したがって、4つのガベージコレクターのタイプは次の通りです:
シリアルGC
これは、シングルスレッドシステムと小さなヒープサイズ向けに設計された、最もシンプルなガベージコレクタです。作業中はすべてのアプリケーションが凍結されます。-XX:+UseSerialGC JVMオプションを使用してオンにすることができます。
並行/スループット GC
これはJDK 8のJVMのデフォルトのコレクタです。その名前が示すように、複数のスレッドを使用してヒープ領域をスキャンし、コンパクションを行います。このコレクタの欠点は、マイナーコレクションやフルGCを実行する際にアプリケーションスレッドを一時停止させることです。このコレクタは、そのような一時停止を処理できるアプリケーションに最適であり、コレクタによって引き起こされるCPUオーバーヘッドを最適化しようとする場合に適しています。
CMSコレクター
CMSガベージコレクタ(”concurrent-mark-sweep”)アルゴリズムは、未使用のオブジェクトを再利用するためにヒープをスキャンするために複数のスレッド(”concurrent”)を使用します。このコレクタは、2つのケースで停止-ザ-ワールド(STW)モードに入ります:-ルートの初期マーキング(つまり、スレッドの入り口や静的変数から到達可能な旧世代のオブジェクトの初期マーキングを行う際)-アプリケーションがアルゴリズムが並行して実行されている間にヒープの状態を変更し、正しいオブジェクトがマークされるように最終的な処理を行う必要がある場合。このコレクタはプロモーションの失敗に直面する可能性があります。若い世代から旧世代に移動するオブジェクトがあり、コレクタが旧世代のスペースを確保するための十分な時間を持っていない場合、プロモーションの失敗が発生します。これを防ぐためには、旧世代へのより多くのヒープサイズを提供するか、コレクタにより多くのバックグラウンドスレッドを提供することができます。
G1コレクター
![garbage collection in java, Java GC](https://cdn.silicloud.com/blog-img/blog/img/65648d48daa94e2bdf7bce42/14-0.png)
-
- 若年世代のみのフェーズ:このフェーズでは、若年世代のオブジェクトのみを含み、それらを老年世代に昇格させます。若年世代のみのフェーズと空間回収フェーズの遷移は、老年世代が一定の閾値で占有されるときに開始されます。この時、G1は通常の若年世代のコレクションではなく、初期マークの若年世代コレクションをスケジュールします。
初期マーキング:このタイプのコレクションは、通常の若年世代のコレクションに加えてマーキングプロセスを開始します。並行マーキングにより、老年世代領域の現在の生存オブジェクトが決定され、次の空間回収フェーズで保持されます。マーキングが完全に終了する前に、通常の若年世代のコレクションが発生することがあります。マーキングは、RemarkとCleanupの2つの特別なストップ・ザ・ワールドの一時停止で終了します。
Remark:この一時停止は、マーキング自体を終了し、グローバルな参照の処理とクラスのアンロードを行います。RemarkとCleanupの間で、G1は並行して生存情報の要約を計算し、これがCleanupの一時停止で最終化されて内部データ構造の更新に使用されます。
Cleanup:この一時停止では、完全に空の領域も処理し、実際に空間回収フェーズが続くかどうかを判断します。空間回収フェーズが続く場合、若年世代のみのフェーズは単一の若年世代のコレクションで終了します。
空間回収フェーズ:このフェーズは、複数のミックスドコレクションで構成されます。若年世代領域に加えて、老年世代領域の生存オブジェクトも移動します。空間回収フェーズは、G1がさらに老年世代領域を移動しても十分な空き領域が得られないと判断するまで続きます。
G1は、-XX:+UseG1GCフラグを使用して有効にすることができます。この戦略により、バックグラウンドスレッドが到達不可能なオブジェクトをスキャンを終える前にヒープが枯渇する可能性が減ります。また、G1はヒープを逐次コンパクト化することができますが、CMSコレクタはSTWモードでのみ実行できます。Java 8では、G1コレクタによる美しい最適化が提供されており、ストリングの重複排除と呼ばれています。私たちのストリングを表す文字配列は、ヒープスペースの大部分を占有していることがわかっています。新しい最適化では、G1コレクタがヒープ全体で複数回複製されている文字列を特定し、それらを同じ内部char[]配列を指すように変更することで、ヒープに同じ文字列の複数のコピーが不必要に存在するのを避けることができます。この最適化を有効にするには、-XX:+UseStringDeduplication JVM引数を使用することができます。G1はJDK 9でデフォルトのガベージコレクタです。
Java 8 の PermGen と Metaspace
Java 8では、Permanent Generationスペースが削除されたため、JDK 8 HotSpot JVMではクラスのメタデータの表現にネイティブメモリが使用されるようになりました。これをMetaspaceと呼びます。クラスのメタデータのほとんどの割り当てはネイティブメモリから行われます。また、クラスのメタデータに使用するメモリ量を制限するために、MaxMetaspaceSizeという新しいフラグが追加されました。これを指定しない場合、実行中のアプリケーションの需要に応じてMetaspaceが動的にリサイズされます。クラスのメタデータの使用量がMaxMetaspaceSizeの制限に達すると、Metaspaceのガベージコレクションがトリガーされます。Metaspaceのガベージコレクションが過剰に行われる場合、クラスやクラスローダーのメモリリークやアプリケーションの適切なサイズ指定の問題の症状と言えます。これでJavaにおけるガベージコレクションについての説明は終わりです。Javaにはさまざまなガベージコレクターが存在することについて、理解していただけたと思います。参考文献:Oracleドキュメンテーション、G1 GC。