この記事では、まずガベージ コレクションの一般的な方法を簡単に紹介し、次に G1 コレクターのコレクション原理、他のガベージ コレクターと比較したその利点を分析し、最後にいくつかのチューニングの実践方法を示します。
まず、G1 を理解する前に、ガベージコレクションとは何なのかを明確に理解する必要があります。簡単に言えば、ガベージ コレクションは、メモリ内で使用されなくなったオブジェクトをリサイクルすることです。 。 ルート検索アルゴリズムの基本的な考え方は、「GC ルート」という名前の一連のオブジェクトを開始点として使用し、これらのノードから下方向に検索することです。このパスは参照チェーンと呼ばれます。オブジェクトには GC ルートに接続された参照チェーンがありません。これで、オブジェクトが使用できないことがわかります。これらのオブジェクトが占有しているメモリを解放する方法 コピーや直接クリーニングも含まれますが、直接クリーニングするとメモリの断片化が発生するため、一般に 3 種類のリサイクル アルゴリズムが生まれます 1. 使用可能なメモリ容量を分割します。 2 つの同じサイズのブロックに分割し、一度に 1 つだけを使用します。残ったオブジェクトを別のブロックにコピーし、使用済みのメモリ領域を一度にクリアします。その利点は、実装が簡単で、効率が高く、メモリの断片化がないことです。欠点は、管理に 2 倍のメモリが必要なことです。 2. マーキングとクリアのアルゴリズムは、「マーキング」と「クリア」の 2 つの段階に分かれています。まず、必要なオブジェクトをマークします。欠点は、メモリの断片化が起こりやすいことです。 マーキング操作は、「マーククリーン」アルゴリズムと一致します。後続の操作は、オブジェクトを直接クリーンアップするだけではなく、不要なオブジェクトがクリーンアップされた後、残っているすべてのオブジェクトをクリアすることです。オブジェクトが移動されるため、効率は低くなります。 「mark-clean」ですが、メモリの断片化は発生しません 生成の前提に基づいています オブジェクトの生存時間により時間は変化するため、生存時間が長いオブジェクトの場合は、その回数を減らします。 gc は、このようにして、メモリを新しい世代と古い世代に分割し、新しく作成されたオブジェクトを保存し、古い世代は存続期間の長いオブジェクトを保存します。 、若い世代のみがクリーンアップされ、古い世代は必要な場合にのみクリーンアップされるため、GC 効率が大幅に向上し、 Java ガベージ コレクターの歴史 G1 の最初の論文 (付録 1) は 2004 年に出版され、2012 年には jdk1.7u4 でのみ利用可能でした。オラクルは正式に、G1 を jdk9 のデフォルトのガベージ コレクターにして、CMS を置き換えることを計画しています。オラクルが G1 を強く推奨する理由は何ですか? G1 の利点は何ですか? まず第一に、G1 の設計原則はシンプルで実現可能なパフォーマンスチューニングです 開発者は次のパラメータを宣言するだけで済みます: -XX:+UseG1GC -Xmx32g -XX:MaxGCPauseMillis=200 where -XX: + UseG1GC は G1 ガベージ コレクターをオンにし、-Xmx32g は最大ヒープ メモリを 32G に設計し、-XX:MaxGCPauseMillis=200 は最大 GC 一時停止時間を 200 ミリ秒に設定します。チューニングが必要な場合、メモリ サイズが決まっている場合は、最大一時停止時間を変更するだけで済みます。 第二に、G1は新世代と旧世代の間の物理的空間分割を解除します。 これにより、各世代を個別のスペースにセットアップする必要がなくなり、各世代に十分なメモリがあるかどうかを心配する必要がなくなります。 代わりに、G1 アルゴリズムはヒープを複数の領域 (リージョン) に分割しますが、これらの領域は引き続き世代別コレクターに属します。ただし、これらの領域の一部には新世代も含まれます。新世代のガベージ コレクションでは、すべてのアプリケーション スレッドを一時停止し、生き残ったオブジェクトを旧世代または Survivor スペースにコピーする方法が引き続き使用されます。旧世代も多くのエリアに分割されており、G1 コレクターはオブジェクトをあるエリアから別のエリアにコピーすることでクリーンアップ作業を完了します。これは、通常の処理中に G1 がヒープ (少なくともヒープの一部) の圧縮を完了するため、cms メモリの断片化の問題が発生しないことを意味します。 G1にはヒューモンガスエリアと呼ばれる特別なエリアもあります。 オブジェクトがパーティション容量の 50% を超えて占有している場合、G1 コレクターはそれを巨大オブジェクトとみなします。これらの巨大オブジェクトはデフォルトで古い世代に直接割り当てられますが、存続期間が短い巨大オブジェクトの場合、ガベージ コレクターに悪影響を及ぼします。この問題を解決するために、G1 は巨大なオブジェクトを格納するために使用される Humongous エリアを分割します。巨大なオブジェクトが 1 つの H パーティションに収まらない場合、G1 はそれを格納するために連続した H パーティションを探します。連続した H 領域を見つけるために、Full GC を開始する必要がある場合があります。 追記: Java 8 では、永続世代も通常のヒープメモリ空間に移動され、メタ空間に変更されました。 オブジェクト割り当て戦略 大きなオブジェクトの割り当てといえば、オブジェクト割り当て戦略について話さなければなりません。 TLAB (Thread Local Allocation Buffer) スレッドローカル割り当てバッファ Eden 領域割り当て 膨大な領域割り当て TLAB はスレッドローカル割り当てバッファであり、その目的はオブジェクトをできるだけ早く割り当てます。オブジェクトが共有スペースに割り当てられている場合は、何らかの同期メカニズムを使用して、これらのスペース内の空きスペース ポインターを管理する必要があります。 Eden 空間では、各スレッドにオブジェクトを割り当てるための固定パーティション、つまり TLAB があります。オブジェクトを割り当てるとき、スレッド間の同期は必要ありません。 TLAB 空間に割り当てられないオブジェクトの場合、JVM はそれらを Eden 空間に割り当てようとします。 Eden スペースにオブジェクトを収容できない場合は、古い世代でのみスペースを割り当てることができます。 最後に、G1 は 2 つの GC モード、Young GC と Mixed GC を提供します。どちらも Stop The World (STW) です。以下では、これら 2 つのモードをそれぞれ紹介します。 ヤングGCは主にエデンエリアでGCを実行し、エデンスペースがなくなると発動します。この場合、Eden 空間のデータは Survivor 空間に移動されます。Survivor 空間が足りない場合は、Eden 空間のデータの一部が直接旧世代空間に昇格されます。 Survivor 領域のデータは新しい Survivor 領域に移動され、一部のデータも旧世代領域に昇格されます。最終的に、Eden スペース内のデータは空になり、GC は動作を停止し、アプリケーション スレッドは実行を継続します。 この時点で、新しい世代のオブジェクトのみを GC する場合、すべてのルート オブジェクトをどのように見つけるかという問題を検討する必要があります。 旧世代のオブジェクトはすべてルートですか?このようにスキャンするとかなり時間がかかります。そこで、G1 は RSet の概念を導入しました。その正式名は Remembered Set で、その機能は特定のヒープ領域を指すオブジェクト参照を追跡することです。 CMS には、RSet という概念もあります。古い世代には、新しい世代への参照を記録する領域があります。これは指摘事項です。Young GC を実行する場合、ルートをスキャンするときは、この領域のみをスキャンする必要があり、古い世代全体をスキャンする必要はありません。 ただし、G1 では、1 つのパーティションが小さすぎ、パーティションが多すぎるため、ポイントアウトは使用されません。一部のパーティション参照では GC が必要なくなります。まったくスキャンされました。そこで、G1 ではそれを解決するためにポイントインが使用されます。ポイントインとは、どのパーティションが現在のパーティション内のオブジェクトを参照しているかを意味します。このように、これらのオブジェクトをルートとしてのみスキャンすることで、無効なスキャンが回避されます。若い世代が複数いるため、新しい世代間の参照を記録する必要がありますか? GC が発生するたびにすべての新しい世代がスキャンされるため、古い世代から新しい世代への参照のみを記録する必要があるため、これは不要です。 参照されるオブジェクトが多い場合、アサイナーは各参照を処理する必要があり、アサイナーのオーバーヘッドが非常に大きくなることに注意してください。アサイナーのオーバーヘッドの問題を解決するために、G1 カードには別の概念が導入されています。テーブル(カードテーブル)。カード テーブルはパーティションを固定サイズの連続領域に論理的に分割し、各領域をカードと呼びます。通常、カードは 128 ~ 512 バイトと小さくなります。 Card Table は通常、バイト配列 であり、各パーティションの空間アドレスは Card のインデックス (つまり、配列の添字) によって識別されます。デフォルトでは、各カードは参照されません。アドレス空間が参照されると、このアドレス空間に対応する配列インデックスの値は「0」としてマークされ、つまりダーティおよび参照済みとしてマークされます。また、RSet は配列の添字も記録します。通常の状況では、この RSet は実際にはハッシュ テーブルであり、キーは別の領域の開始アドレス、値はセット、および内部の要素はカード テーブルのインデックスです。 静的オブジェクトとローカル オブジェクトがスキャンされる ダーティ カード キューを更新する RS から検出する若い世代は古い世代のオブジェクトを指します 生き残ったオブジェクトを生存/古い領域にコピーします ソフト参照、弱参照、仮想参照処理 この段階では、G1 GC がルートをマークします。このフェーズは、通常の (STW) 若い世代のガベージ コレクションと密接に関連しています。 G1 GC は、最初にマークされた生存領域内の古い世代への参照をスキャンし、参照されたオブジェクトをマークします。このフェーズはアプリケーション (非 STW) と同時に実行され、このフェーズが完了した後でのみ、次の STW 若い世代のガベージ コレクションを開始できます。 G1 GC は、ヒープ全体でアクセス可能な (ライブ) オブジェクトを検索します。このフェーズはアプリケーションと同時に実行され、STW の若い世代のガベージ コレクションによって中断される可能性があります。 このフェーズは、マーキング サイクルの完了に役立つ STW コレクションです。 G1 GC は SATB バッファをクリアし、アクセスされていないライブ オブジェクトを追跡し、参照処理を実行します。 この最終フェーズでは、G1 GC は統計と RSet 精製の STW 操作を実行します。会計期間中に、G1 GC は完全に無料の領域と混合ガベージ コレクションに使用できる領域を識別します。クリーンアップ フェーズは、空の領域をリセットして空きリストに戻すときに、部分的に同時に行われます。 グレーからトラバースを続け、サブオブジェクトをスキャンしたオブジェクトを黒に設定します。 すべての到達可能なオブジェクトを通過した後、すべての到達可能なオブジェクトが黒に変わります。到達できないオブジェクトは白く表示されるため、クリーンアップする必要があります。 これは素晴らしく見えますが、マーキングプロセス中にアプリケーションが実行されている場合、オブジェクトのポインタが変化する可能性があります。この場合、問題が発生します: オブジェクト損失問題 ガベージ コレクターが次の状況をスキャンするときの状況を見てみましょう: このとき、アプリケーションは次の操作を実行します: A.c + 現時点では、C は白であり、クリーンアップする必要があるゴミとみなされます。これは明らかに不合理です。では、アプリケーションの実行中に GC でマークされたオブジェクトが失われないようにするにはどうすればよいでしょうか?考えられる方法は 2 つあります: 挿入時にオブジェクトを記録する これは、CMS と G1 の 2 つの異なる実装方法に対応しています: CMS で採用されている増分更新は、増分更新です。更新。白いオブジェクトへの参照が書き込みバリア内の黒いオブジェクトのフィールドに割り当てられていることが判明した場合、白いオブジェクトは灰色に変わります。つまり、挿入時に記録されます。 1. 存続オブジェクトのマークを開始するときにスナップショット マークを生成します。同時マーキング中、変更されたすべてのオブジェクトがキューに入れられます (書き込みバリアでは、古い参照によって指定されたすべてのオブジェクトが非白になります) この方法で, G1 は、どの古いパーティションが最も多くのゴミをリサイクルできるかを知ることができるようになりました。 グローバル同時マーキングが完了すると、ある瞬間から Mix GC が開始されます。これらのガベージ コレクションは、通常の若い世代のガベージ コレクションを実行するだけでなく、バックグラウンド スキャン スレッドによってマークされたいくつかのパーティションも収集するため、「ハイブリッド」と呼ばれます。ハイブリッド ガベージ コレクションは次のとおりです。 この時点で、ハイブリッド GC は終了しました。次のセクションでは、チューニングの練習をしていきます。 MaxGCPauseMillis チューニング GC を使用するための最も基本的なパラメーターは以前に紹介しました: 若い GC: 新しい世代のすべてのリージョンを選択します。新しい世代のリージョンの数を制御することで、若い GC のコストを制御します。 これを理解すると、最大一時停止時間を設定するのが簡単になります。 まず、許容できる最大一時停止時間には制限があり、この制限内に設定する必要があります。しかし、どのような値を設定すればよいのでしょうか?スループットと MaxGCPauseMillis の間でバランスを取る必要があります。 MaxGCPauseMillis の設定が小さすぎると、GC が頻繁に発生し、スループットが低下します。 MaxGCPauseMillis の設定が大きすぎると、アプリケーションの一時停止時間が長くなります。 G1 のデフォルトの一時停止時間は 200 ミリ秒です。ここから開始して適切な時間を調整できます。 その他の調整パラメータ -XX:G1HeapRegionSize=n -XX:ParallelGCThreads=n 論理プロセッサが 8 つを超える場合は、n の値を論理プロセッサ数の約 5/8 に設定します。これは、n の値が論理プロセッサ数の約 5/16 になる可能性がある大規模な SPARC システムを除いて、ほとんどの場合に機能します。 -XX:ConcGCThreads=n 並行してマークされたスレッドの数を設定します。 n を、並列ガベージ コレクション スレッド (ParallelGCThreads) の数の約 1/4 に設定します。 -XX:InitiatingHeapOccupancyPercent=45 マーキング サイクルをトリガーする Java ヒープ占有のしきい値を設定します。デフォルトの占有率は、Java ヒープ全体の 45% です。 次のパラメータの使用は避けてください: -Xmn オプションまたは -XX:NewRatio などのその他の関連オプションを使用して、若い世代のサイズを明示的に設定することは避けてください。若い世代のサイズが固定され、一時停止時間の目標が上書きされます。 フル GC をトリガーする 場合によっては、G1 はフル GC をトリガーし、シリアル コレクターを使用して GC 作業を完了し、GC を一時停止します。時間は第 2 レベルに到達します。アプリケーション全体が一時停止アニメーション状態にあり、リクエストを処理できません。もちろん、プログラムはこれを望んでいません。では、フル GC はどのような状況で発生するのでしょうか? 同時モードの失敗 G1 はマーキング サイクルを開始しますが、Mix GC の前に古い世代がいっぱいになり、この時点で G1 はマーキング サイクルを放棄します。この場合、ヒープサイズを増やすか、サイクルを調整する(スレッド数 -XX:ConcGCThreads を増やすなど)必要があります。 プロモーションの失敗または退避の失敗 GC の実行時に、G1 には存続オブジェクトまたは昇格されたオブジェクトを保存するための十分なメモリがないため、フル GC がトリガーされます。ログで (スペースが枯渇した) または (スペースがオーバーフローした) を確認できます。この問題を解決する方法は次のとおりです: a、-XX:G1ReservePercent オプションの値を増やして (それに応じて合計ヒープ サイズを増やして)、「ターゲット領域」用に予約されているメモリの量を増やします。 b、-XX:InitiatingHeapOccupancyPercent を減らして、マーキング サイクルを早めに開始します。 c、-XX:ConcGCThreads オプションの値を増やすことで、並列マーキング スレッドの数を増やすこともできます。 巨大オブジェクトの割り当て失敗 巨大オブジェクトが割り当てに適した領域を見つけられない場合、領域を解放するためにフル GC が開始されます。この場合、巨大オブジェクトが巨大オブジェクトでなくなるように、大量の巨大オブジェクトの割り当てを避けるか、メモリを増やすか、-XX:G1HeapRegionSize を増やす必要があります。 スペースが限られているため、G1 のチューニング方法は数多くあるため、ここでは 1 つずつ列挙しません。毎日の練習でゆっくりと検討してください。最後に、Java 9 の正式リリースを楽しみにしています。デフォルトで G1 をガベージ コレクターとして使用する Java のパフォーマンスは再び向上しますか? 参照
第二に、G1 を理解する
三、G1ヤングGC
Four、G1 Mix GC
Mix GC は、通常の新世代のガベージ コレクションを実行するだけでなく、バックグラウンド スキャン スレッドによってマークされたいくつかの古い世代のパーティションも再利用します。 その GC ステップは 2 つのステップに分かれています:
Mix GC を実行する前に、グローバル同時マーキング (グローバル同時マーキング) ) 最初に同時マーキングが実行されます)。グローバル同時マーキングの実行プロセスはどのようなものですか? G1 GC では、主に混合 GC のマーキング サービスを提供しており、GC プロセスには必須の部分ではありません。グローバル同時マーキングの実行プロセスは 5 つのステップに分かれています:
3色マーキングアルゴリズム同時マーキングに関しては、同時マーキングの3色マーキングアルゴリズムを理解する必要があります。これはトレース コレクターを記述する便利な方法であり、コレクターの正確性を推定するために使用できます。 まず、オブジェクトを 3 つのタイプに分類します。
G1 では、削除時にすべてのオブジェクトを記録するために STAB (snapshot-at-the-begining) メソッドが使用されます。
ハイブリッド GC は、コピー クリーニング戦略も使用します。GC が完了すると、スペースが再び解放されます。
混合 GC: 新世代のすべてのリージョンに加えて、グローバルな同時マーキング統計に基づいて、コレクション収入の高いいくつかの旧世代リージョンを選択します。ユーザーが指定したコスト目標の範囲内で、可能な限り収益の高い旧世代リージョンを選択します。
G1 領域セットのサイズ。値は 2 の累乗で、範囲は 1 MB ~ 32 MB です。目標は、Java ヒープの最小サイズに基づいて約 2048 の領域を分割することです。
以上がJava G1 ガベージ コレクターの解析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。