blog

JVM - ガベージ・コレクション・メカニズム - ガベージ・コレクター

ごみコレクターは仕様で過度に規定されておらず、ベンダーやJVMのバージョンによって実装が異なります。 JDKのバージョンは高速な反復プロセスであるため、Javaの開発は数多くのGCバージョンを生み出し...

Feb 23, 2020 · 31 min. read
シェア

I. GCロジックの分類

ゴミ・コレクターは仕様で過度に規定されておらず、ベンダーやJVMのバージョンによって実装される可能性があります。

JDKのバージョンは高速な反復プロセスであるため、Javaの開発は数多くのGCバージョンを生み出してきました。

ゴミ収集人をさまざまな角度から分析すると、GCをさまざまなタイプに分類することができます。

スレッド数別

それは直列の屑コレクターおよび平行の屑コレクターに分けることができます

シリアル・リカバリー
  • シリアルリサイクルとは、ゴミ収集が終了するまでワーカースレッドが一時停止している同じ時間帯に、1つのCPUだけがゴミ収集動作に使用できることを意味します。
    • シングル CPU プロセッサや小さなアプリケーション メモリなど、ハードウェア プラットフォームが特に好ましくない状況では、シリアル リサイクルの方がパラレル リサイクラーやコンカレント リサイクラーよりも性能が高くなります。そのため、クライアント・モードのクライアント側JVMでは、デフォルトでシリアル・リサイクルが使用されます。
    • 並行性の高いCPUでは、パラレル・リサイクラーはシリアル・リサイクラーよりもストール時間が短くなります。
パラレルリサイクル
  • シリアル・リサイクルとは対照的に、パラレル・コレクションでは複数のCPUが同時にゴミ収集を行うことができます。これによりアプリケーションのスループットが向上しますが、並列収集は依然として排他的であり、シリアル収集と同様に「Stop-the-world」メカニズムを使用します。

瓦礫の処理方法によって、圧縮廃棄物リサイクル業者と非圧縮廃棄物リサイクル業者に分けられます。

  • コンパクト・ゴミ回収業者は、リサイクル完了後、リサイクルされたゴミをなくすために、残された物をコンパクトに整理します。
    • ポインタの衝突を利用してオブジェクト空間を再割り当てします。
  • 非コンパクト式ゴミ収集業者はこのステップを行いません。
    • 空きリストを使用して、オブジェクト空間を再割り当てします。

仕事の記憶間隔に従って

さらに分類すると、若い世代のゴミ収集業者と古い世代のゴミ収集業者に分けられます。

次に、GC

  • スループット:総実行時間に対するユーザーコードの実行時間の割合
    • 総運転時間:プログラムの運転時間+メモリー再生時間
  • ゴミ収集オーバーヘッド:スループットの補数で、総実行時間に対するゴミ収集に費やされる時間の比率。
  • サスペンド時間:ゴミ収集が行われている間、プログラムのワーカースレッドがサスペンドしている時間。
  • 収集頻度:アプリケーションの実行に対して収集操作が発生する頻度。
  • メモリー・フットプリント:Javaヒープが占有するメモリー量。
  • 高速:オブジェクトが作成されてからリサイクルされるまでの時間。
  • この3つを合わせると、"不可能な三角形 "になります。この3つの総合的な性能は、技術の向上とともにどんどん良くなっていきます。優秀なコレクターは通常、このうち多くても2つを同時に満たします。
  • この3つのうち、一時停止時間の重要性が増しています。これは、ハードウェアの進化に伴い、より多くのメモリが許容できるようになり、ハードウェアの性能向上は、実行中のアプリケーションに対するコレクタの影響を軽減するのに役立つ、すなわちスループットを向上させるからです。一方、メモリの拡張はレイテンシに悪影響を及ぼします。
  • 要するに、捕捉すべきポイントは2つあります:
    • スループット
    • 一時停止時間

スループット

  • スループットとは、CPUが消費する総時間に対する、CPUがユーザーコードを実行している時間の割合です。
    • 例えば、VMが100分間実行され、ゴミ収集に1分かかる場合、スループットは99%です。
  • この場合、アプリケーションはより長い休止時間を許容できるため、高スループットのアプリケーションはより長い時間のベンチマークを持ち、高速応答を考慮する必要はありません。
  • スループット第一、つまりSTWの単位時間当たりの最短時間:0.2 + 0.2 = 0.4

一時停止時間

  • 「一時停止時間 "は、GC スレッドが実行できるようにするために、アプリケーションスレッド が一時停止される時間です。
    • たとえば、GC 中に 100 ミリ秒の一時停止時間は、その 100 ミリ秒の間、アプリケーションスレッドがアクティブでないことを意味します...
  • 一時停止時間優先とは、1本のSTWを可能な限り短くすること:0.1+0.1+0.1+0.1+0.1=0.5

高いスループットと低い暫定性

  • スループットが高ければ高いほど、アプリケーションのエンドユーザはアプリケーションのスレッドだけが「生産的」な作業をしていると感じることができるためです。直感的には、スループットが高ければ高いほど、アプリケーションの実行速度は速くなります。
  • なぜなら、エンドユーザーの視点からは、アプリがハングアップする原因が GC 2011 であろうと他の何かであろうと、常に悪いことだからです。アプリケーションのタイプにもよりますが、200ミリ秒の短い一時停止でさえ、エンドユーザ・エクスペリエンスを中断してしまうことがあります。そのため、特に対話型アプリケーションでは、一時停止時間を短くしたり長くしたりすることは非常に重要です。
  • 残念ながら、「高スループット」と「低休止時間」は相反する目標です。
    • スループットを優先する場合、メモリ・リコールの頻度を減らす必要がありますが、その結果、GCはメモリ・リコールを実行するために長い休止時間を必要とすることになります。
    • 一方、低レイテンシを優先する場合は、各リコールの一時停止時間を短縮するためにメモリ・リコールを頻繁に実行する必要がありますが、これは逆にプログラムのスループットの低下につながります。
  • GC 2011アルゴリズムを設計する際に識別されなければならない目的: GC 2011アルゴリズムは、2つの目的のうちの1つだけをターゲットにすることができます。2つの間の妥協点を見つけるようにしてください。
  • 最大スループットが優先される場合のダウンタイムを短縮します。

III.ごみ収集業者の概要

仮想マシンでは、ゴミを回収する仕組みが必要で、それをガベージコレクションと呼び、対応する製品をガベージコレクタと呼びます。

  • 1999年のJDK 1.3.1に搭載されたシリアルGCは、最初のGCでした。 ParNewゴミコレクタは、シリアルコレクタのマルチスレッド版です。
  • パラレルGCはJDK6以降、HotSpotのデフォルトGCになりました。
  • 2012年、G1はJDKバージョン1.7u4で利用可能になりました。
  • 2017年、G1はCMSに代わってJDK9のデフォルトのゴミ収集機になりました。
  • シリアル・リサイクラー:シリアル、シリアル・オールド

-------------------------------------------- Watershed --------------------------------------------

  • 2018年9月、JDK11がリリース。Epsilon rubbish collectorの導入。"No one 0p "コレクタとしても知られています。また、ZGC:スケーラブルな低レイテンシごみコレクタの導入。
  • 2019年3月、JDK12がリリース。 未使用のヒープメモリをOSに自動返却するG1を強化。また、Shenandoah GCを導入:低ダウンタイムGC 。
  • 2019年9月、JDK13がリリース。未使用のヒープメモリをOSに自動返却するZGCを強化。
  • 2020年3月、JDK14リリース。CMSのゴミコレクターを削除。macOSとWindowsでZGCを拡張。

古典的なゴミ収集人

  • ホールスタック・コレクター:G1
  • パラレルリサイクラー:ParNew、Parallel Scavenge、Parallel Old
  • 同時使用リサイクラー:CMS、G1

古典的なゴミ収集業者とゴミ代替の関係

  • JDK 14媒体:廃棄されたParallel ScavengeとSerialOld GCの組み合わせ
  • 高齢者コレクター:シリアルオールド、パラレルオールド、CMS
  • ホールスタックコレクター: G1

ごみ収集業者の組み合わせ関係

  • シリアルオールドは、CMSの "Concurrent Mode Failure "失敗時のバックアッププランとして使用されます。
  • -XX:+PrintCommandLineFlags
  • JDK 14では:Parallel ScavengeとSerialOld GCの組み合わせの非推奨
  • -XX:+UseParallelGC:新世代がパラレルGCを使用していることを示します。

なぜコレクターがたくさん必要なのですか? Javaは、モバイル、サーバーなど、さまざまな場面で使用されるからです。そのため、ゴミ収集のパフォーマンスを向上させるために、シナリオごとに異なるゴミ収集器を提供する必要があります。

コレクター間の比較は行われますが、最高のコレクターを選ぶためのものではありません。すべてのシナリオではたらく完全なコレクターとしてそのようなものがない、ましてやone-size-fits-allコレクター。従って選択は特定の適用のための最もよいコレクターについてのだけです。

デフォルトのゴミ収集機を見る

  • -xx: +PrintCommandLineFlags: コマンドライン関連のパラメータを表示します。
  • コマンドラインコマンドを使用します: jinfo a フラグ関連ごみコレクターパラメータプロセスID

ガベージ・コレクターの切り替え

  • -XX:+プリントコマンドラインフラグ
  • -XX:+UseParNewGC:新世代がParNew GCを使用するようにマークします。
  • -XX:+UseParallelGC:新世代がパラレルGCを使用することを示します。
  • -XX:+UseParallelOldGC : 古い GC の並列使用を示します。
    • 注:この2つは互いに有効化することができます。
  • XX:+UseConcMarkSweepGC:古い世代のCMS GCの使用を示します。

IV.シリアル・リサイクラー:連続リサイクル

  • jdk1.3は、新世代のリサイクルのための唯一の選択肢です。
  • シリアルコレクターは、HotSpotのクライアントモードでデフォルトのフレッシュマンごみコレクターとして機能します。
  • シリアルコレクタは、コピーアルゴリズム、シリアル再生成、および "Stop the World "メカニズムを使用してメモリ再生を実行します。
  • このコレクターはシングルスレッド・コレクターですが、「シングルスレッド」という意味は、単に1つのCPUや1つの収集スレッドだけでゴミ収集を完了するという意味ではなく、より重要なのは、ゴミ収集を行っている間、収集が終わるまで他のすべてのスレッドの作業を一時停止しなければならないということです。

Serial Old

シリアル・コレクターは、若い世代に加えて、古い世代のゴミ回収を行うシリアル・オールド・コレクターも提供しています。 シリアル・オールド・コレクターは、シリアル・リサイクルと「ストップ・ザ・ワールド」メカニズムも使用します。

  • メモリ再生アルゴリズムだけがマークワン圧縮アルゴリズムを使用しています。
  • シリアルオールドは、クライアントモードで動作するデフォルトの老朽化したゴミ収集機です。
  • Serial 0OdはServerモードで主に2つの用途があります:
    • 新世代の ParallelScavenge との併用。
    • 旧世代のCMSコレクターのバックアップごみ収集ソリューションとして。

Serialリサイクラーの利点

  • シンプルで効率的なSeria1コレクタは、シングルCPUに限定された環境では、スレッドインタラクションのオーバーヘッドがないため、ゴミ収集に集中することで、シングルスレッドで最高の収集効率を自然に達成します。
    • クライアントモードで動作する仮想マシンは良い選択です。
  • ユーザーのデスクトップ・アプリケーションのシナリオでは、利用可能なメモリは一般に、比較的短時間でゴミを回収できる程度に小さく、これが頻繁に起こらない限り、シリアル・リサイクラーの使用は許容されます。
  • HotSpot仮想マシンでは、-XX: +UseSerialGCパラメータを使用して、若い世代と古い世代の両方にシリアルコレクタを使用するように指定します。
    • これは、新しい世代にシリアルGCを使用し、古い世代にシリアルオールドGCを使用することと同じです。

Serialリサイクラーの概要

  • この手のゴミコレクターはみんな知っていますが、今ではシリアルを使わなくなりました。しかもシングルコアCPU限定。今はシングルコアではありません。
  • よりインタラクティブなアプリケーションでは、このごみコレクタは受け入れられません。シリアルごみコレクタは、一般的にJavawebアプリケーションでは使用されません。

V. ParNewリサイクラー:パラレルリサイクル

  • Serial GCが若い世代におけるシングルスレッドのゴミコレクターだとすれば、ParNewコレクターはSerialコレクターのマルチスレッド版です。
    • ParはParallelの略で、New:は新世代しか扱えません。
  • ParNewコレクターが並列でメモリ再生を行うことと、ParNewコレクターが若い世代で同じ複製アルゴリズムである「Stop the World」メカニズムを使用すること以外は、2つのゴミコレクターの違いはほとんどありません。
  • ParNewは、サーバー・モードで動作する新世代の多くのJVMのデフォルトのゴミ・コレクターです。

新世代の場合、リサイクルの回数が多いので、並列アプローチを使うのが効率的です。

高齢者の場合、リコールは少なく、連続的なアプローチを使うことでリソースを節約できます。

ParNewコレクター効率

  • ParNew コレクターは並列リカバリーに基づいているため、どのようなシナリオでも ParNew コレクターの方が Serial コレクターよりも効率的であると結論付けてもよいですか?

    • ParNewコレクターはマルチCPU環境で動作し、複数のCPUやコアなどの物理的なハードウェアリソースをフルに活用できるため、より迅速にゴミ収集を完了し、プログラムのスループットを向上させることができます。
    • しかし、シングル CPU 環境では、ParNew コレクターは Serial コレクターよりも効率的ではありません。シリアルコレクタはシリアルリサイクルをベースにしていますが、CPUが頻繁にタスク切り替えを行う必要がないため、マルチスレッドインタラクション時に発生する余分なオーバーヘッドを効果的に回避することができます。
  • シリアルオールドに加え、パーニューGCは現在CMSコレクターと提携しています。

  • プログラムでは、開発者は"-XX: +UseParNewGC "オプションを使用して、メモリ再利用タスクに.ParNewコレクタを使用することを手動で指定できます。これは、若い世代が並列コレクタを使用することを示し、古い世代には影響しません。

  • -XX: ParallelGCThreads スレッド数を制限します。デフォルトは CPU データと同じ数のスレッドを有効にします。

パラレルリサイクラー:スループット第一

Parallel Scavengeコレクターは、並列リサイクルに基づくParNewコレクターに加え、レプリケーションアルゴリズム、並列リサイクル、HotSpotの若い世代では「Stop the World」メカニズムも使用しています。では、Parallelコレクターは余計なものなのでしょうか?

  • ParNew コレクターとは異なり、Parallel Scavenge コレクターの目標は、管理可能なスループットを達成することです。
  • アダプティブ・チューニング戦略もParallel ScavengeとParNewの重要な違いです。

高スループットは、CPU時間を効率的に使用してプログラムの計算をできるだけ早く完了させることができ、主にバックグラウンドで実行され、多くのインタラクションを必要としないタスクに適しています。そのため、サーバー環境でよく使用されます。例えば、バッチ処理、注文処理、給与支払い、科学計算を行うアプリケーションなどです。

Parallel Old

  • Parallel コレクターは JDK 1.6 で提供され、古い時代の Serial Old コレクターの代わりに古い時代のゴミ収集を行います。
  • パラレル・オールド・コレクターは、マーク・アンド・コンパクト・アルゴリズムを使用しますが、パラレル・リサイクルと「Stop-the-World」メカニズムにも基づいています。
  • プログラムのスループットが優先されるアプリケーション・シナリオでは、パラレル・コレクタとパラレル・オールド・コレクタの組み合わせにより、サーバー・モードで優れたメモリ再生パフォーマンスが得られます。
  • Java 8では、デフォルトはこのゴミのようなコレクターです。

並列ガベージコレクタのパラメータ構成

  • -XX: +UseParallelGC は、若い世代が Parallel 並列コレクタを使用してメモリ再生成タスクを実行することを手動で指定します。
  • -XX: +UseParallel0ldGc は、古い世代が並列リカバリコレクタを使用することを手動で指定します。
    • 新しい世代と古い世代にそれぞれ適用されます。デフォルトのjdk8はオンです。
    • 上記の2つのパラメータについて、1つはデフォルトでオンになり、もう1つもオンになります。
  • -XX: ParallelGCThreads は、若い世代の並列コレクタのスレッド数を設定します。一般的には、スレッドが多すぎてゴミ収集の性能に影響を与えないように、CPUの数と同じにするのがベストです。
    • デフォルトでは、CPU数が8未満の場合、Paralle lGCThreadsの値はCPU数と等しくなります。
    • CPU 数が 8 より大きい場合、ParalleLGCThreads の値は 3 + [5*CPU_ Count]/8 になります。]
  • -XX : MaxGCPau3eMillis は、ゴミ収集機の最大休止時間を設定します。単位はミリ秒です。
    • 一時停止時間をできるだけ MaxGCPauseMills 以内に保つために、.NET Framework のコレクタでは、一時停止時間の最大値を設定します。Javaのヒープ・サイズやその他のパラメータは、動作中に調整されます。
    • ユーザーにとっては、一時停止時間が短ければ短いほど使い勝手がよくなります。しかしサーバー側では、高い同時実行性と全体的なスループットが重視されます。ですから、サーバー側はパラレルやコントロールに適しています。
    • パラメータの使用には注意が必要です。
  • -XX: GCTimeRatio(ゴミ収集時間の総時間に対する割合)は、スループットの指標として使用されます。
    • 値の範囲を取ります。デフォルト値は 99 で、これはゴミ収集時間が 1 を超えないことを意味します。
    • 前の -XX: MaxGCPauseMillis パラメータとは多少矛盾します。休止時間が長いほど、Radio パラメータが設定されたパーセンテージを超えやすくなります。

CMS リサイクラー:低レイテンシー

JDK 1.5では、HotSpotは高度にインタラクティブなアプリケーションのために、ほとんど画期的なゴミコレクタを導入しました。CMSコレクタは、HotSpot VMで初の真の同時実行コレクタであり、ゴミコレクションスレッドが初めてユーザスレッドと同時に動作することを可能にしました。

  • CMS コレクターの焦点は、ゴミ収集中のユーザースレッドのダウンタイムを最小化することです。ダウンタイムが短ければ短いほど、アプリケーションはユーザーとの対話に適しており、応答時間が長ければ長いほど、ユーザーエクスペリエンスが向上します。現在、Javaアプリケーションの大部分は、インターネットのウェブサイトやB/Sシステムのサーバー側に集中しており、これらのアプリケーションは、サービスの応答性に特に注意を払い、ユーザーに良い体験をもたらすために、システムのダウンタイムが最短になることを望んでいます。CMSガベージコレクタは、この種のアプリケーションのニーズに非常に合致しています。CMSのガベージコレクションアルゴリズムは、マークアンドクリーンアルゴリズムと、"ストップザワールド "を採用しています。
  • 残念ながら、古い世代のコレクターであるCMSは、JDK 1.4.0にすでに存在する新しい世代のコレクターであるParallel Scavengeとは動作しないため、JDK 1.5で古い世代のコレクターとしてCMSを使用する場合、新しい世代のコレクターはParNewまたはSerialコレクターのいずれかに限定されます。
  • G1の登場時、CMSの使用はまだ非常に広まっていました。今日に至るまで、CMS GC 2011 を使用する多くのシステムが存在しています。

CMS実行プロセス

CMSの全プロセスはコレクターのそれよりも複雑で、全プロセスは主に4つのフェーズ、すなわち、最初のマーキングフェーズ、同時マーキングフェーズ、再マーキングフェーズ、同時クリアリングフェーズに分かれています。

  • 初期ラベリングフェーズ:このフェーズでは、.NET Frameworkの "Stop World "メカニズムにより、プログラム内のすべての作業スレッドが一時的に中断されます。「このフェーズの主なタスクは、GCRootsが直接関連付けることができるオブジェクトをマークすることだけです。マーキングが完了すると、中断されたアプリケーションは.スレッド。直接関連付けられるオブジェクトは比較的小さいので、これは非常に高速です。
  • 同時マーキングフェーズ:GCルートに直接関連するオブジェクトからオブジェクトグラフ全体を走査するプロセスで、このプロセスには長い時間がかかりますが、ユーザースレッドを停止する必要はなく、ごみ収集スレッドと同時に実行できます。
  • 再タギングフェーズ:プログラムのワーカースレッドとゴミ収集スレッドは、同時タギングフェーズで同時またはクロスオーバーに実行されるため、このフェーズの一時停止時間は、通常、初期タギングフェーズより若干長くなりますが、同時タギングフェーズよりはるかに短くなります。
  • 並行クリアフェーズ:このフェーズでは、マーキングフェーズでデッドと判定されたオブジェクトをクリーンアップして削除し、メモリ領域を解放します。生き残ったオブジェクトを移動させる必要がないため、このフェーズはユーザースレッドと並行することもできます。

CMS特徴分析

  • 最も時間のかかるコンカレント・マーキングとコンカレント・クリアのフェーズでは作業を一時停止する必要がないため、全体的なリカバリーは低停止で済みます。
  • CMSコレクターは並行リサイクルを使用しますが、初期化タグ付けと再タグ付けフェーズの間、アプリケーションの作業スレッドを一時停止するために「Stop-the-World」メカニズムを実行する必要があります。
  • つまり、現在のすべてのゴミ収集機は、「世界を止める」必要性をまったくなくしているわけではなく、一時停止時間をできるだけ短くしているだけだということがわかります。
  • さらに、ユーザースレッドはゴミ収集フェーズ中に中断されないので、CMS再生プロセス中にアプリケーションユーザースレッドが十分なメモリを利用できることも保証する必要があります。そのため、CMSコレクタは他のコレクタのように古い年齢がほぼ完全に埋まるまで待つことはできず、ヒープメモリ使用量がある閾値に達したときに再生を開始します。予約されたメモリがCMS動作中のアプリケーションに十分でない場合、"Concurrent Mode Failure "が発生し、仮想マシンはバックアッププランを開始します:古い世代のゴミを再回収するために、一時的にシリアル0ldコレクタを有効にします。この結果、非常に長い休止時間が発生します。
  • CMSコレクタのゴミ収集アルゴリズムはマーク・アンド・クリーン・アルゴリズムであり、各メモリ再利用の後、いくつかのメモリ断片が必然的に生成されます。そうすると、CMSは新しいオブジェクトのためにメモリ空間を確保するときにポインタ衝突技術を使うことができず、空きリストを選択してメモリ確保を実行することしかできなくなります。

Mark Sweepはメモリの断片化を引き起こすので、アルゴリズムをMark Compactに変更してはどうでしょうか?

なぜなら、同時実行がクリアされたときに Compact を使用してメモリを整理すると、元のユーザー スレッドが使用していたメモリはどのように使用できるのでしょうか。ユーザー・スレッドが実行を継続できるように、その実行リソースの前提は影響を受けないため、Mark Compactは「Stop the World」シナリオに適しています。

CMS

  • コンカレントコレクション
  • 低遅延

CMS

  • メモリの断片化が発生し、同時クリア後にユーザー・スレッドが使用できる領域が不足します。大きなオブジェクトを割り当てることができない場合、フルGCを早期に起動する必要があります。
  • CMS コレクターは CPU リソースに非常に敏感です。同時実行フェーズの間、ユーザがストールすることはありませんが、アプリケーションを遅くし、スレッドの一部を占有するため、総スループットが低下します。
  • CMS コレクターはフローティング・ゴミを処理できません。Concurrent Mode Failure "が発生し、別のFull GCが発生する可能性があります。浮遊ゴミ:同時マーキングフェーズ中、ワーカースレッドとゴミ収集スレッドは同時または交差して実行されているため、同時マーキングフェーズ中に新しいゴミオブジェクトが生成された場合、CMSはこれらのゴミオブジェクトをマーキングすることができず、最終的にこれらの新しいゴミオブジェクトは時間内に回復されず、回復されなかったメモリは次のGC中に解放されるしかありません。
  • リラベリングの段階にはゴミの修正も含まれていますが、それでもゴミが浮いているのはなぜですか?
    • リラベリングとは、すでにラベリングされたオブジェクトを検出することです。ゴミになる可能性のあるオブジェクトは、他のオブジェクトから再び参照されるため、ゴミが取り除かれたと誤解されるのを防ぐためにリラベリングする必要があります。一方、浮遊ゴミは、最初はマーキングされず、同時マーキングフェーズの後にゴミになる可能性のあるオブジェクトによって生成された新しいゴミを指します

CMSパラメーター設定

  • -XX: +UseConcMarkSweepGc CMS コレクタを使用してメモリ再生タスクを実行するように 手動で指定します。
    • このパラメータをオンにすると、自動的に XX: +UseParNewGc がオンになります。つまり、ParNew +CMS +Serial 0ld の組み合わせです。
  • -XX: CMS1ni tiatingOccupanyFraction は、ヒープ・メモリ使用量のしきい値を設定し、そのしきい値に達するとすぐに再要求を開始します。
    • JDK5 およびそれ以前のバージョンのデフォルト値は 68 です。つまり、古い時代のメモリ使用量が 68 に達すると、.CMS の再生成が開始されます。CMSの再利用が開始されます。 JDK6 5以上のデフォルト値は92です。
    • メモリの増加が遅い場合は、少し大きな値を設定することができます。 大きなしきい値は、CMSトリガーの頻度を効果的に減らすことができ、古い年代の再要求の数を減らすことで、アプリケーションのパフォーマンスをより大幅に向上させることができます。一方、アプリケーションのメモリ使用量が急速に増加している場合は、古い年齢のシリアル・コレクタを頻繁にトリガしないように、しきい値を低くする必要があります。したがって、このオプションはフルGCの実行回数を効果的に減らすことができます。
  • 問題は、メモリの断片化を回避できること。しかし、メモリのデフラグ処理を同時に行うことができないため、ストール時間が長くなるという問題があります。
  • -XX: ParallelCMSThreads CMS のスレッド数を設定します
    • CMS が起動するスレッド数のデフォルトは /4 です。 ParallelGCThreads は若い世代の並列コレクタのスレッド数です。CPUリソースが逼迫している場合、CMsコレクタのスレッドの影響を受けると、ゴミ収集フェーズでアプリケーションのパフォーマンスが非常に悪くなることがあります。

後続バージョンのJDKにおけるCMSの変更

  • JDK9 新機能: CMS は非推奨です!
    • JDK 9 以降の HotSpot VM でパラメータ -XX:+UseConcMarkSweepGC を使用して CMS コレクターをオンにすると、将来 CMS が非推奨になるという警告メッセージが表示されます。
  • JDK14新機能:CMSゴミコレクターの削除。
    • JDK14で-XX: +UseConcMarkSweepGCを使用すると、JVMはエラーを報告せず、警告メッセージを出すだけで、終了しません。

G1 リサイクラー:地域代替型

すでに強力なGCがいくつも目の前にあるのに、なぜGarbage First GCをリリースするのでしょうか?

その理由は、アプリケーションが扱う業務が大規模化・複雑化し、利用者も増えているため、GCがなければアプリケーションが正常に進行することが保証されず、STWの原因となることが多いGCでは実際の需要に追いつかないため、GCの最適化が常に試みられていることにあります。Java7のアップデート4以降に導入された新しいゴミコレクタであるG1ゴミコレクタは、現在のコレクタの開発における最先端の成果の1つです。技術開発における最先端の成果の一つです。

同時に、拡大し続けるメモリと増え続けるプロセッサに対応するためには、良好なスループットを維持しつつ、一時停止時間をさらに短縮する必要があります。

G1の正式な目標は、管理可能なレイテンシで可能な限り高いスループットを達成することであり、それゆえ「フル機能のコレクター」としての期待が寄せられています。

なぜゴミファーストと呼ばれるのですか?

  • G1は並列リクレーマーなので、ヒープメモリを多くの無相関領域(物理的に不連続)に分割します。異なる領域は、エデン、生存者0領域、生存者1領域、古い年齢などを表現するために使用されます
  • G1 GCは、Javaヒープ全体でリージョン全体のゴミ収集を系統的に回避します。G1は、各リージョン内に蓄積されたゴミの値の大きさを追跡し、バックグラウンドで優先順位リストを維持し、許容される収集時間に基づいて、毎回、最も高い値を持つリージョンを最初にリサイクルします。
  • このアプローチでは、ゴミの量が最も多い区間を回収することに重点を置いているため、G1という名前が付けられています
  • G1は、主にマルチコアCPUと大容量メモリを搭載したマシンをターゲットとしたサーバーサイド・アプリケーション向けのゴミ収集装置で、GCのダウンタイムを満たす確率が非常に高く、高いスループット性能も備えています。
  • CMS コレクターと Parallel + Parallel 0ld の組み合わせに代わって、JDK 9 以降のデフォルトのごみコレクターです。オラクルによって公式に「フル機能のごみコレクタ」と呼ばれています。
  • 一方、CMS は JDK 9 で非推奨とされました。
  • G1はまだjdk8のデフォルトのゴミコレクターではないため、XX:+UseG1GCを使用して有効にする必要があります。

G1ゴミ収集機の利点

並列性と並行性
  • 並列処理:G1はリサイクル中に複数のGcスレッドを同時に動作させ、マルチコアの計算能力を効果的に活用することができます。このとき、ユーザ・スレッドSTW
  • 同時実行:G1にはアプリケーションと交互に実行する機能があり、アプリケーションと同時に実行できる作業もあります。
分割世代コレクション
  • 世代という観点から見ると、G1は依然として世代ゴミ収集機に属し、若い世代と古い世代を区別し、若い世代は依然としてエデン領域とサバイバー領域を持っています。しかし、ヒープの構造上、エデンゾーン全体、ヤングジェネレーション、オールドジェネレーションが連続している必要はありません。
  • ヒープ空間を論理的に若い世代と古い世代を含む領域に分割します。
  • 様々なタイプのリサイクル業者とは異なり、若い世代と年配の世代の両方に対応しています。他のリサイクル業者が若い世代か年配の世代のどちらかを対象にしているのとは対照的です;
空間統合
  • CMS:マーク・アンド・イレース・アルゴリズム、メモリ断片化、数回のGC後のデフラグメント
  • G1はメモリーをリージョンに分割し、リージョンを基本単位としてメモリーを再生します。リージョン間はレプリケーション・アルゴリズムです
  • しかし、全体は実際には1つの圧縮アルゴリズムをマークとみなすことができ、両方のアルゴリズムは、メモリの断片化を避けることができます。この機能は、プログラムが長時間実行することを容易にし、大きなオブジェクトの割り当ては、連続したメモリ空間を見つけることができないため、早期に次のGCをトリガすることはありません。
予測可能な停止時間のモデリング

これは、CMSと比較した場合のG1のもう1つの大きな利点です。CMSでは、休止時間を短くすることに加えて、休止時間を予測可能なモデルにしているため、ユーザーは、長さMミリ秒のタイムスライス内でゴミ収集にNミリ秒以上を消費しないように明示的に指定することができます。

  • パーティショニングにより、G1はメモリ再利用のために領域の一部のみを選択することができ、再利用の範囲が狭まるため、グローバルなストール状況の発生をよりよく制御することができます。
  • G1は、各リージョン内のゴミの山の大きさを追跡し、バックグラウンドで優先リストを保持し、許容される収集時間に従って、毎回、最も大きな値を持つリージョンを最初にリサイクルします。
  • CMSGCと比べると、G1はCMSのようなディレイドストップはベストケースではできないかもしれませんが、ワーストケースでははるかに優れています。

G1

  • CMSと比較して、G1は圧倒的な優位性を持っているわけではありません。例えば、G1はゴミ収集のためのメモリフットプリントが大きく、ユーザーのアプリケーション実行時の実行負荷もCMSより高い。
  • 経験的に、CMSはメモリが少ないアプリケーションではG1を上回る可能性が高く、一方、G1はメモリが多いアプリケーションで強みを発揮します。バランスは6GBと8GBの間です。

G1パラメーター設定

  • -XX: +UseG1GC メモリ再利用タスクに G1 コレクタを使用するように手動で指定します。
  • -XX: G1HeapRegionSize 各リージョンのサイズを設定します。値は2のべき乗で、1MBから32MBの範囲で、最小のJavaヒープ・サイズに基づいて約2048のリージョンを分割することを目標としています。デフォルトは、ヒープ・メモリの 1/2000 です。
  • -XX: MaxGCPauseMillis Gc休止時間の最大値を設定します。デフォルト値は 200 ミリ秒です。
  • -XX: ParallelGCThread ワーカースレッド数の STW 値を設定します。最大8
  • -XX: ConcGCThreads 同時にタグ付けされるスレッド数を設定します。n はゴミ収集スレッドの並列数の 1/4 程度に設定してください。
  • -XX: Ini tiatingHeapOccupancyPercent 同時 GC サイクルをトリガするための Java ヒープ占有しきい値を設定します。この値を超えると GC 2011 がトリガーされます。デフォルト値は 45 です。

G1リサイクラーの共通作業手順

G1の設計原則は、JVMのパフォーマンス・チューニングを簡素化することであり、開発者は3つの簡単なステップだけでチューニングを完了できます:

ステップ1: G1ゴミ・コレクタをオンにします。

ステップ2:最大ヒープ・メモリの設定

ステップ3:最大ストール時間の設定

G1には、YoungGC、Mixed GC、Full GCの3つのゴミ収集モードがあり、それぞれ異なる条件でトリガーされます。

G1適用シナリオ

  • 大容量メモリとマルチプロセッサを搭載したマシンをターゲットとするサーバーサイド・アプリケーション向け。
  • 上位のアプリケーションは、低GCレイテンシを必要とし、ソリューションを提供する大規模なスタックを持つものです。
  • 例えば、予測可能な休止時間は、ヒープサイズが約6GB以上の場合、0.5秒未満になります。
  • 以下の場合は、CMS よりも G1 を使用した方がよいでしょう:
    • Javaヒープの50%以上がアクティブなデータで占められている場合;
    • オブジェクトの割り当てやエイジ・ブーストの頻度が大きく異なる場合;
    • GCストールが長すぎる
  • G1を除くHotSpotのゴミコレクターは、組み込みのJVMスレッドを使用してGCマルチスレッド処理を実行します。
  • G1 GC 2011は、バックグラウンドで実行されるGC作業を引き受けるためにアプリケーションスレッドを使用することができます。

分割領域、ゼロアウト

G1コレクタを使用する場合、Javaヒープ全体を同じサイズの約2048個の独立したRegionブロックに分割し、各Regionブロックのサイズはヒープ空間の実際のサイズに依存し、全体は1MBと32MBの間で制御され、2のN乗、すなわち、1MB、2MB、4MB、8MB、1 6MB、32MBです。

XX:G1HeapRegionSizeで設定できます。すべてのリージョンは同じサイズを持ち、JVMのライフタイム中に変更されることはありません。

新世代と旧世代の概念は残りますが、新世代と旧世代はもはや物理的に分離されたものではなく、どちらもリージョンの集合体です。論理的連続性は、リージョンの動的割り当てによって達成されます。

  • リージョンはエデン、サバイバー、オールド/テニュアメモリのリージョンに属することができます。しかし、一つのリージョンは一つの役割にしか属しません。図中の E はそのリージョンが Eden メモリーリージョンに属していることを示し、 s は Survivor メモリーリージョンに属していることを示し、 O は Old メモリーリージョンに属していることを示します。図中の空白は未使用のメモリ領域を示します。
  • G1ゴミコレクタはまた、図のHブロックのようなHumongousメモリ領域と呼ばれる新しいタイプのメモリ領域を追加します。これは主に大きなオブジェクトを格納するために使用され、1.5regionを超える場合はH.
  • Hを設定した理由:ヒープにある大きなオブジェクトは、デフォルトでは古い年代にそのまま割り当てられますが、短期間しか存在しない大きなオブジェクトだと、ゴミ収集機に悪影響を及ぼします。この問題を解決するために、G1では大型オブジェクト専用のHumongousゾーンを分割しています。H領域がラージオブジェクトに適合しない場合、G1はそれを格納するための連続したH領域を探します。時には、連続するHゾーンを見つけるためにFull GCを開始しなければなりません。

G1 リサイクラーによるゴミ収集プロセス

G1 GCのゴミ収集プロセスは、主に以下の3つで構成されています:

  • ヤング・ジェネレーションGC
  • 老齢同時ラベリングプロセス
  • ミックスリサイクル

1.アプリケーションはメモリを確保し、若い世代のEden領域がなくなった時点で若い世代の再生成プロセスを開始します。G1の若い世代の収集フェーズは並列排他コレクタです。G1 の若い世代の回収フェーズは並列排他コレクタです。若い世代の回収フェーズの間、G1 GC はすべてのアプリケーショ ンスレッドを一時停止し、若い世代の回収を実行するために複数のスレッドを起動します。その後、生き残ったオブジェクトは、若い世代のインターバルからSurvivorインターバルまたは古い世代のインターバルに移動されます。

2.ヒープメモリの使用量がある値に達すると、古い時代の同時実行マーク処理が開始されます。

3.完成した馬に印をつけます。混合回収プロセスが直ちに開始されます。混合回収期間では、G1 GCは、生存しているオブジェクトを古いインターバルから空きインターバルに移動させ、これらの空きインターバルは、古い年代の一部となります。若い世代とは異なり、G1の旧世代リサイクラーは、旧世代全体をリサイクルする必要はなく、旧世代領域の一部だけを一度にスキャン/リサイクルする必要があります。同時に、この古い領域は若い領域と一緒にリサイクルされます。

例として:ウェブサーバーは、Javaプロセスの最大ヒープメモリは4G、毎分1500リクエストに応答し、45秒ごとに新しいメモリの約2Gが割り当てられます。 G1は、若い世代のために45秒ごとにリサイクルされ、31時間ごとに全体のヒープは、使用率の45%に達すると、それは、タグ付けが完了し、その後、混合リサイクルの4〜5回を開始する同時タグ付けプロセスの古い世代を開始します。

メモリセットとライトバリア

  • 異なるリージョンから参照されるオブジェクトに関する問題
  • リージョンは分離できません。リージョン内のオブジェクトは、他のリージョン内のオブジェクトから参照される可能性があります。オブジェクトの生存を決定する場合、正確性を確保するためにJavaヒープ全体をスキャンする必要がありますか?
  • 他のスプリット・ジェネレーション・コレクターにも、この問題があります。
  • 新しい世代も同時に古い世代をスキャンしなければならなかったのですか?
  • これではMinorGCの効率が落ちてしまいます。

解決策

  • G1であろうと他のスプリット・ジェネレーション・コレクターであろうと、JVMはグローバル・スキャンを避けるためにRememberedSetを使用しています。
  • 各リージョンには対応するリメンバーセットがあります。
  • 参照タイプのデータ書き込み操作が行われるたびに、書き込みバリアが生成され、操作が一時的に中断されます。
  • 次に、参照が書き込まれるオブジェクトが、Reference型のデータとは異なるRegionにあることを確認します。
  • 異なる場合は、CardTable を介して、参照がオブジェクトを指す Region に対応する Membered Set に、関連する参照情報を記録します。
  • GCルートノードの列挙範囲にRemembered Setを追加することで、ゴミ収集が実行される場合、グローバルスキャンがなく、ミスもないことが保証されます。

IX.G1リカバリープロセス詳細

ヤング・ジェネレーションGC

  • JVMが起動すると、まずG1がエデン領域を準備し、プログラムは実行中にエデン領域にオブジェクトを生成し続けます。 エデン領域がなくなると、G1は若い世代のゴミ収集処理を開始します。
  • ゴミ収集の若い世代は、エデンゾーンとサバイバーゾーンのみをリサイクルします。
  • YGCの間、まずG1がアプリケーションの実行を停止し、G1がバックコレクションを作成します。バックコレクションは、再生する必要のあるメモリセグメントの集まりで、若い世代の再生プロセスのバックコレクションには、若い世代のエデンゾーンとサバイバーゾーンのすべてのメモリセグメントが含まれます。
  • レプリケーション・アルゴリズム

その後、以下のようにリサイクル処理を開始します:

  • 第一段階、ルートのスキャン
    • ルートとは、静的変数が指すオブジェクト、実行中のメソッド呼び出しの連鎖の中のローカル変数などです。ルート参照は、RSetによって記録された外部参照とともに、生存するオブジェクトをスキャンするためのエントリーポイントとして機能します。
  • フェーズ 2: RSet の更新

    ダーティカードキューのカードを処理し、RSetを更新します。 このフェーズが完了すると、RSetは、オブジェクトが配置されているメモリセグメント内のオブジェクトへの古い年代の参照を正確に反映します。
    • ダーティ・カード・キュー:アプリケーションの参照割り当てステートメントobject.field=objectに応答して、JVMは特別な操作を実行し、オブジェクトの参照情報を持つダーティ・カード・キューのカードをキューに入れます。
    • 若い世代のリサイクル時に、G1はダーティ・カード・キューのすべてのカードを処理してRSetを更新し、RSetがリアルタイムで参照関係を正確に反映するようにします。
    • では、なぜ参照割り当てステートメントで直接RSetを更新しないのでしょうか?これはパフォーマンス上の理由からです。 RSetの処理にはスレッド同期が必要で、これは大きなオーバーヘッドとなります。
  • 第三段階では、RSetを処理。
    • エデン内のオブジェクトのうち、老齢オブジェクトによって指されたオブジェクトを特定し、エデン内のこれらの指されたオブジェクトを生存オブジェクトとみなします。
  • 第4段階では、オブジェクトがコピーされます。
    • この段階では、オブジェクト・ツリーが走査され、Eden メモリ・セグメントで生存しているオブジェクトが、Survivor 領域の空のメモリ・セグメントにコピーされます。 Survivor 領域で生存しているオブジェクトの年齢が閾値に達していなければ、年齢が 1 ずつ増加し、閾値に達していれば、01d 領域の空のメモリ・セグメントにコピーされます。Survivor領域が十分でない場合、Eden領域のデータの一部が古いage領域に直接昇格されます。
  • ステージ5、引用の処理
    • Soft、Weak、Phantom、Final、JNI Weakなどの参照の処理。最終的にEden空間は空になり、GCは動作を停止しますが、ターゲットメモリ内のオブジェクトは断片化されることなく連続的に格納されるため、コピー処理はメモリ整理の効果を達成し、断片化を減らすことができます。

若い世代のGC+コンカレント・タギング・プロセス

  • 初期ラベリングフェーズ:ルートノードから直接到達可能なオブジェクトにラベルを付けます。このフェーズはSTWであり、若い世代のGCをトリガします。
  • Root Region Scanning : G1 GC は、Survivor 領域に直接到達可能な Old Age 領域のオブジェ クトをスキャンし、参照されたオブジェクトをマークします。このプロセスは、若い GC で完了する必要があります。
  • コンカレント・タギング:コンカレント・タギングはヒープ全体で行われます。コンカレントタギングフェーズでは、リージョンオブジェクト内のすべてのオブジェクトがゴミであることが判明した場合、そのリージョンは直ちにリサイクルされます。同時に、コンカレントマーキングプロセスは、各リージョンのオブジェクトアクティビティを計算します。
  • 再タギング:アプリケーションの継続に伴い、前回のタグ付けの結果を修正する必要があります。G1では、CMSよりも高速な初期スナップショットアルゴリズムが使用されています。
  • 排他的クリーンアップ:各領域で生存しているオブジェクトとGC回収の割合を計算し、並べ替え、混合して回収できる領域を特定します。次のフェーズのためのステージ設定。STWです。
    • このフェーズでは、実際にはゴミ収集は行いません
  • 同時清掃段階:完全に空っぽになった場所を特定し、清掃。

ミックスリサイクル

より多くのオブジェクトがOldRegionに昇格すると、ヒープ・メモリを使い果たすのを避けるために、VMは混合ゴミ・コレクタ、すなわちOldGCではない混合GCを起動し、ヤング・リージョン全体と0ldRegionの一部をリサイクルします。ここで重要なのは、OldRegionのすべてではなく、OldRegionの一部であるということです。どのOldRegionを収集するかを選択することで、ゴミ収集時間をコントロールすることができます。また、Mixed GC は Full GC ではないことにも注意してください。

  • 同時実行マークが終了すると、100%ゴミとなった古い時代のメモリセグメントが再生され、部分的にゴミとなったメモリセグメントがカウントされます。デフォルトでは、これらの古いメモリセグメントは 8 つのインスタンスで再生成されます。
  • 古い年代のメモリセグメントはデフォルトで 8 回で再生されるので、G1 はゴミの多いメモリセグメントを先に再生します。メモリセグメントに占めるゴミの割合が高いほど、先に再生されます。xX: G1MixedGCLiveThresholdPercent, デフォルトは 65%, つまり、メモリセグメントに対するゴミの割合が 65% にならないと、メモリセグメントは再生されません。ゴミの割合が低すぎる場合は、生き残っているオブジェクトの割合が高く、コピーに時間がかかることを意味します。
  • 混合リサイクルは8回行う必要はありません。しきい値IXx: G1HeapWastePercentがあり、デフォルト値は10%で、これはヒープメモリ全体の10%が浪費されることを許容することを意味し、再生可能なヒープメモリの割合が10%未満であることが判明した場合、混合再生はもはや実行されないことを意味します。GCは多くの時間を要しますが、ほとんどメモリを回復しないからです。

Full GC

G1 の本来の意図は、Full GC を回避することです。しかし、上記が適切に機能しない場合、G1 はアプリケーションの実行を停止し、ゴミ収集のためにシングルスレッド・メモリ回復アルゴリズムを使用しますが、これは非常にパフォーマンスが悪く、アプリケーションは長時間ストールすることになります。フル GC は避けるべきであり、フル GC が発生したら調整が必要です。フル GC はどのような場合に発生するのでしょうか?たとえば、G1 が生きているオブジェクトをコピーしているときに、ヒープ・メモリが小さすぎ て空のメモリ・セグメントがない場合、フル GC に戻ってしまいます。G1Full GCには2つの原因が考えられます:

  • 避難所には、推進されたものを保管する十分なスペースがありません;
  • 同時並行プロセスでスペースが枯渇。

G1リサイクラーの最適化に関する推奨事項

  • 若い世代のサイズ
    • OneXmnやOneXX:NewRatioのような関連オプションを使用して、ヤングジェネレーションのサイズを明示的に設定することは避けてください。
    • 若い世代のサイズを固定すると、一時停止時間のターゲットが上書きされます。
  • 一時停止時間の目標に厳しくなりすぎないように
    • G1 GC 2011のスループット目標は、アプリケーション時間の90%とゴミ収集時間の10%です。
    • G1 GC 2011のスループットを評価する場合、一時停止時間の目標をあまり積極的にしすぎないでくだ さい。アグレッシブすぎる目標は、スループットに直接影響するゴミ収集オー バーヘッドをより多く発生させることをいとわないことを意味します。

X. 7つのクラシックなゴミ収集機のまとめ

ゴミ収集業者の選び方

  • Javaのゴミ・コレクターの設定は、JVMの最適化にとって重要な選択であり、適切なゴミ・コレクターを選択することで、JVMのパフォーマンスに大きな違いが生まれます。
  • ゴミ収集業者の選び方は?
    • ヒープ・サイズの変更を優先して、JVMが完了するように適応させます。
    • メモリが100M以下なら、シリアル・コレクタを使いましょう。
    • シングルコア、シングルコンピュータのプログラムで、ダウンタイムが必要ない場合は、シリアル・コレクター
    • マルチCPUで、高いスループットが必要で、1秒以上のダウンタイムが許される場合は、並列またはJVMを選択します。
    • マルチCPUの場合、低ダウンタイムの追求、高速応答の必要性は、同時コレクタの使用
    • 公式には、G1、高いパフォーマンスをお勧めします。現在、インターネットプロジェクトは、基本的にG1を使用します。
  • 最後にもうひとつ、はっきりさせておかなければならないことがあります:
    • 最高のコレクター、ましてや万能のコレクターなど存在しません;
    • チューニングは常に特定のシナリオ、特定のニーズのために行われます。

XI.革命的ZGC

ZGCは、どのようなヒープ・メモリ・サイズでも、ゴミ収集の休止時間を10ミリ秒未満に制限し、スループットへの影響をできるだけ少なくする低レイテンシを達成するという目標において、Shenandoahと非常によく似ています。

ZGCコレクタは、Regionメモリレイアウトに基づき、世代を使用せず、リードバリア、カラーポインタ、メモリマルチマッピングなどのテクニックを使用して、低レイテンシを主目標とする並行マークアコンプレッサアルゴリズムを実装するゴミコレクタです。

ZGCの作業プロセスは4つの段階に分けられます:同時ラベリング、同時準備再配置、同時再配置、同時再マッピングなど

ZGCは、sTWである最初のタグ付けを除いて、ほとんどすべての場所で同時実行されます。そのため、ダウンタイムは.そのほとんどすべてが最初のマーキングのために費やされ、その部分の実際の時間はごくわずかです。

ZGCの最強の停止時間テストでは、パラレル、G1に2桁の差をつけました。平均休止時間、958休止時間、998休止時間、99.98休止時間、最大休止時間のいずれにおいても、ZGCは10ミリ秒以内を維持するのに苦労しません。

Read next

vueのソースコードに深く潜って、vueの双方向データバインディングの原理を理解する。

データの双方向バインディングを実現するには、まずデータをハイジャックしてリッスンする必要があります。属性が変更された場合、サブスクライバのWatcherに更新が必要かどうかを伝える必要があります。 多くのサブスクライバが存在するため、これらのサブスクライバを特別に収集するメッセージサブスクライバ Dep が必要で、リスナー Observ...

Feb 23, 2020 · 2 min read