JDK 16 G1/Parallel GC changes

原文はこちら。
The original article was written by Thomas Schatzl (OpenJDK developer, Oracle).
https://tschatzl.github.io/2021/03/12/jdk16-g1-parallel-gc-changes.html

この記事では、JDK 16 HotspotのStop-the-Worldガベージコレクターのうち、G1とParallel GCの最も重要な変更点をまとめています。

まず、GCサブコンポーネント全体について簡単に説明します。今回のガベージコレクション分野の唯一のJEPは、JEP 376のZGCに関連するもので、スレッドスタック処理がstop-the-worldのポーズ時間中に実施しなくなりました。その結果、現在では1ミリ秒以下のポーズ時間になっています。

JEP 376: ZGC: Concurrent Thread-Stack Processing
https://openjdk.java.net/jeps/376

G1とParallelの両GCもいくつかの興味深い小規模なアップデートが行われました。Hotspot GCサブコンポーネント全体の変更点のリストはこちらで、合計325の変更点があります。

Parallel GC

JDK-8252221の変更で、コミット後のメモリのプレタッチが並列化されました。これは、-XX:+AlwaysPreTouchを有効にした巨大なヒープでの起動に役立ちます。JDK-8254699とその関連(同じコードを共有するG1にもあてはまります)では、x64およびARM64プラットフォームにおいてワークパケットのサイズが最適化されました。

Use multiple workers for Parallel GC pre-touching
https://bugs.openjdk.java.net/browse/JDK-8252221
Suboptimal PreTouchParallelChunkSize defaults and limits
https://bugs.openjdk.java.net/browse/JDK-8254699

JDK-8247820でJavaヒープへのより多くの内部参照処理が並列化されました。Parallel GC、G1の両方において、わずかにポーズ時間が短縮される場合があります。

ParallelGC: Process strong OopStorage entries in parallel
https://bugs.openjdk.java.net/browse/JDK-8247820
G1: Process strong OopStorage entries in parallel
https://bugs.openjdk.java.net/browse/JDK-8247819

G1 GC

これまでは、ヒープサイズの最小値と最大値が異なる場合において、GCポーズ中に G1が Java のヒープメモリを多く所有していることに気付いた場合、余剰分に気付いた時点でそのメモリを返還していました。このメモリの量によっては、簡単に数GBのメモリになることもあり、GCポーズがかなり長くなる(あるテストでは50〜100ms以上)ことがありました。JDK-8236926でこの動作を変更し、ポーズの間、G1はどのメモリ領域をどれだけアンコミットすべきかを決定するだけで、実際のアンコミット作業はバックグラウンドタスクのコンカレントフェーズに延期するようになりました。

Concurrently uncommit memory in G1
https://bugs.openjdk.java.net/browse/JDK-8236926

このバックグラウンドタスクは、これらの領域をインクリメンタルに処理します。インクリメントのサイズは、各ステップが最大でも数ミリ秒しかかからないようになっており、GCのポーズ開始の長い待ち時間を避けることができます。

デバッグレベルで gc+heap ログを有効にすることで、G1の作業を追跡できます。Concurrent Commitを含むメッセージでは、チャンクのサイズやステップの継続時間など、G1が行った作業に関する詳細な情報を得られます。詳細については、リリースノートをご覧ください。

Concurrently Uncommit Memory in G1 (JDK-8236926)
https://jdk.java.net/16/release-notes#JDK-8236926

JDK-8240556で、G1が多くの巨大なオブジェクトをできるだけ多く回収できる場合(JDK 9 JDK-8048179で導入された機能)の同時マーキング(concurrent marking)を開始するタイミングの計算の問題に対処しています。

Abort concurrent mark after effective eager reclamation of humongous objects
https://bugs.openjdk.java.net/browse/JDK-8240556
Early reclaim of large objects that are referenced by a few objects
https://bugs.openjdk.java.net/browse/JDK-8048179

GC開始時に、G1は現在のヒープ占有率が内部のしきい値より大きいかどうかに応じて、このガベージコレクションが同時マーキングの準備をすべきかどうかを決定します。同時マーキングが必要な場合、G1はガベージコレクション中にいくつかの追加ステップを実行し、その後の同時マーキングに備えます。このヒューリスティックは、この特定のガベージコレクション中にヒープ占有率が実際に再び内部閾値を下回る可能性があることを考慮していませんでした。そこでこの場合、G1はマーキングをキャンセルし、先に行った作業を元に戻す準備をします(もちろん同時進行です)。この方法は、必要のないときに同時マーキングやいくつかの混合コレクションを行うよりも、はるかに高速でCPU利用率も少なくなります。

同様に、JDK-8245511では、このマーキングを開始する際のヒープ占有率のしきい値を決定する方法が改善されました。

G1 adaptive IHOP does not account for reclamation of humongous objects by young GC
https://bugs.openjdk.java.net/browse/JDK-8245511

このヒープ占有率のしきい値は、old世代での最近の割り当て率と、通常時にマーキングの開始から最初のold世代のガベージコレクションを開始するまでに要する時間を使って計算されます。割り当て率が大きいほど、またマーキング開始からold世代の最初の収集までの時間が長いほど、この閾値は低くなります。すなわちG1のマーキング開始が早まります。

巨大なオブジェクトはold世代では常に直接割り当てられるため、この割り当て率に大きく影響するという問題があります。しかしJDK 9以降では、前述のeager reclamation(できるだけ多くヒープ領域を回収すること)により、これらの巨大なオブジェクトは非常に早く回収され、old世代の全体的な割り当て率は実際には非常に低くなっているため、不必要なコンカレントマークが多く発生していました。今回のJDK-8245511の変更では、eager reclamationをより考慮することによりこの問題を解決しました。JDK-8245511には、その影響を示す素晴らしいグラフが掲載されています。

[JDK-8245511] G1 adaptive IHOP does not account for reclamation of humongous objects by young GC – Java Bug System

Other Noteworthy Changes

さらに、JDK 16の変更点の中には、重要ではあるものの、エンドユーザーにはあまり知られていないものもあります。

JDK 16では、1つのスレッドがオブジェクトを動かさないことを要求している状況(主にJNIを使ったGCのない言語との相互作用のため)において、オブジェクトの固定をサポートするいくつかの準備が追加されました(JDK-8236594でトラッキングされています)。

G1: Provide object pinning
https://bugs.openjdk.java.net/browse/JDK-8236594

ガベージコレクションと、メモリを必要としているためにガベージコレクションを開始したいすべてのJavaアプリケーションスレッドをブロックするのではなく、G1は先に進み、所定の位置に留まる必要があるものを除くすべてのオブジェクトを(さらにその周囲にもいくつかのオブジェクトも)移動させます。

A.Shipilevが、この問題と、対処方法について説明した記事を書いています。

JVM Anatomy Quark #9: JNI Critical and GC Locker
https://shipilev.net/jvm/anatomy-quarks/9-jni-critical-gclocker/
Aleksey Shipilëv
https://twitter.com/shipilev

この記事によると、この変更により、G1の動作がオプション1(GCを完全に無効にする)からオプション3(オブジェクトを含むサブスペースを固定する)に変わります。JDK-8253600とJDK-8253081はこのような準備的な変更の例です。

G1: Fully support pinned regions for full gc
https://bugs.openjdk.java.net/browse/JDK-8253600
G1 fails on stale objects in archived module graph in Open Archive regions
https://bugs.openjdk.java.net/browse/JDK-8253081

G1におけるリージョンベースのオブジェクト固定を可能にするために欠けている作業は、この機能を有効にすることではありません。これは現段階では数行のコードに過ぎませんが、その動作上の影響はまだ十分に検討されていません。初期のテストでは、影響の分からない潜在的な問題がいくつか見つかりましたが、その後、可能性のある解決策についてブレーンストーミングを行いました。もしこの問題をもっと調査して、この提案(もしくはその他も)を試してみたいと思われる方がいらっしゃれば、遠慮なく原文の著者にご連絡ください。

What’s next

すでにOracleのLTSリリースとなるJDK 17の開発に積極的に取り組んでいます。以下は注目されそうな変更点です。いつものように、いかなる種類の保証もありません。これらの機能は、完成してから統合されます!

JDK 17では、G1のJava以外のヒープメモリ使用量が大幅に改善される可能性があり、特に記憶セット(remembered set)のメモリ使用量を減らすための最適化に重点が置かれています。以下の記事でその問題点を紹介しています。

Welcome 20% less memory usage for G1 remembered sets – Prune collection set candidates early
https://tschatzl.github.io/2021/02/26/early-prune.html
https://logico-jp.io/2021/02/28/welcome-20-less-memory-usage-for-g1-remembered-sets-prune-collection-set-candidates-early/

ここで説明されている変更点(JDK-8262185)は、すでにメインラインにプッシュされ、記事で説明されているような影響が出ています。さらに、以前から取り組んでいる追加の変更もあり、これまで以上に記憶セットのメモリ使用量を削減できる可能性があります。ご期待ください。

G1: Prune collection set candidates early
https://bugs.openjdk.java.net/browse/JDK-8262185

Microsoftは、JDK-8257774において、非常に長いガベージコレクションでの退避失敗を避けるために、G1でのプロアクティブなガベージコレクションのスケジューリングについて、非常に興味深い取り組みを行っています。

G1: Trigger collect when free region count drops below threshold to prevent evacuation failures
https://bugs.openjdk.java.net/browse/JDK-8257774

JDK-8262068では、Hamlin Liが、単純なヒューリスティックを使いG1のFull GCを改善しました。

Improve G1 Full GC by skipping compaction for regions with high survival ratio
https://bugs.openjdk.java.net/browse/JDK-8262068

リージョンがほぼいっぱいの状態の場合、リージョンをコンパクトにすることに気を病む必要はありません。それに伴いGCのパフォーマンスが向上します。他のGCでは既に-XX:MarkSweepDeadRatioオプションでこの機能を実装していますが、G1でもこの機能を使用できるようにすると良いと思います。この変更はすでにレビューに出されています。

Thanks go to…

今回の素晴らしいJDKのリリースに貢献してくれた皆さん。次のリリースでお会いしましょう

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト /  変更 )

Google フォト

Google アカウントを使ってコメントしています。 ログアウト /  変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト /  変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト /  変更 )

%s と連携中