ZGC | What’s new in JDK 18

原文はこちら。
The original article was written by Per Lidén (software engineer, Oracle).
https://malloc.se/blog/zgc-jdk18

3月22日にJDK 18がリリースされました。ここ1年ほどはZGCを世代型GCにするための取り組みに注力してきたため、このリリースはZGCについてはかなり静かなリリースになっています。とはいえ、このリリースでは、ZGC関連で37件のバグ修正ならびに機能強化が含まれています。そのうち、興味深いものをいくつかこのエントリで取り上げます。以前のJDKリリースに含まれているZGCの機能や機能強化について詳細を知りたい方は、以下のエントリをご覧ください。

ReleaseEntry
JDK 17https://malloc.se/blog/zgc-jdk17
https://logico-jp.io/2021/10/10/zgc-whats-new-in-jdk-17/
JDK 16https://malloc.se/blog/zgc-jdk16
https://logico-jp.io/2021/03/31/zgc-whats-new-in-jdk-16/
JDK 15https://malloc.se/blog/zgc-jdk15
https://logico-jp.io/2020/09/25/zgc-whats-new-in-jdk-15/
JDK 14https://malloc.se/blog/zgc-jdk14

では、(ZGCの観点から)JDK 18の新機能について説明していきます。

String Deduplication

String deduplication (文字列の重複排除)はJVMの機能 (-XX:+UseStringDeduplication) で、かなり以前からある機能です。これは、Stringオブジェクトの背後にある同一の文字配列を自動的に重複排除することで、Javaヒープメモリの使用量を削減するのに役立ちます。例えば、ヒープ上に2つのStringオブジェクトが存在し、それらが両方とも”Java”という文字を含む配列を指し示している場合、両方のStringオブジェクトが同じ配列を指すよう、一方のStringオブジェクトが変更され、もう一方の配列は到達不可能になり、GCの対象になります。StringオブジェクトはJavaのヒープにおいてかなりの部分を占め、それらの間で重複が見つかることはかなりよくあることゆえ、この機能は、アプリケーションにおいてヒープメモリの使用量をかなり削減するのに役立つ場合があります。

私は2014年にこの機能のJEP (JEP 192) と初期実装を書き、JDK 8u20の一部として出荷しました。これはZGCが生まれる前のことで、初期実装ではG1 GCで文字列重複排除のサポートが追加されただけでした。

JEP 192: String Deduplication in G1
https://openjdk.java.net/jeps/192
8029075: String deduplication in G1
https://github.com/openjdk/jdk/commit/4a4c0fce93fb919383c793983bcf1cc4bfb7b7bc

2021年、Kim Barrettが、文字列重複排除のインフラの重要な部分をオーバーホールしました。全体的なコンセプトは変わらないのですが、文字列重複排除機構がGCとインターフェースで連結する方法は、より一般的になりました。これにより、この機能をG1以外のガベージコレクタに簡単に統合することができるようになりました。その結果、文字列重複排除のサポートは後にSerialGC、ParallalGC、およびZGCに追加されたのです。

8254598: StringDedupTable should use OopStorage
https://github.com/openjdk/jdk/commit/be0a655208f64e076e9e0141fe5dadc862cba981
8272609: Add string deduplication support to SerialGC
https://github.com/openjdk/jdk/commit/e8a289e77d70d31f2f7d1a8dea620062dbdb3e2a
8267185: Add string deduplication support to ParallelGC
https://github.com/openjdk/jdk/commit/fb1dfc6f49f62990aa9988e9d6f7ffd1adf45d8e
8267186: Add string deduplication support to ZGC
https://github.com/openjdk/jdk/commit/abebbe2335a6dc9b12e5f271bf32cdc54f80b660

文字列重複排除についてあまりご存知でないなら、それが実際に何をするもので、そしていつ有効にしたいと思うものか、JEP 192をチェックしてみてください。この文書が少し古くなっていたとしても(たとえば、このJEPはG1についてのみ記述しています)、この機能のポイントは変わりません。

Class Unloading Issue Fixed

ZGC メーリングリストにパフォーマンスの問題についての報告がありました。これは10年前のバグであり、PermGenの削除にまで遡ることが判明しました。

ZGC unstable behaviour on java 17
https://mail.openjdk.java.net/pipermail/zgc-dev/2021-November/001086.html
JEP 122: Remove the Permanent Generation
https://openjdk.java.net/jeps/122

つまり、約10年前にPermGenを削除するパッチで、Inline Cacheクリーニングを扱う関数に変更が加えられていたのです。

6964458: Reimplement class meta-data storage to use native memory
https://github.com/openjdk/jdk/commit/5c58d27aac7b291b879a7a3ff6f39fca25619103

Inline Cacheは、Javaのメソッド呼び出しを高速化するためにJVMが使用する投機的最適化技術です。GCが未使用のクラスとコンパイルされたメソッドをアンロードするとき、いくつかのInline Cacheをクリーニングし、任意のアンロードされたエンティティを参照しないようにする必要があります。

結局のところ、このパッチには、インデントとスコープが混同されているという、小さいけれども重要な編集ミスが含まれていました。パッチを見ただけでは、その間違いを見つけるのは困難でした。なぜなら、問題のコードも移動されていたからです。このミスにより、いくつかのインラインキャッシュが不正にクリーニングされていました。しかし、この不正なクリーニングは、JVMのクラッシュのような明らかな問題を引き起こしまさず、代わりにGCとJavaのスレッドが、これらのキャッシュをどのようにクリーニングするかについて、意見を異にし、争うという悪循環を引き起こしたのです。その結果、特定の条件下では、クラスのアンロードが完了するまでに非常に長い時間がかかることがありました。この問題の根本的な原因は、GCと同時に実行されているJavaスレッドの間の悪い相互作用だったので、(ZGCのような)同時にクラスのアンロードを行うGCだけが影響を受けました。(SerialGC、ParallelGC、G1GCのような)Stop-the-Worldを伴うクラスアンロードを行うGCは、影響を受けませんでした。それは、JavaスレッドがGCと同時に実行されることがないので、この悪い相互作用が発生することがないためです。

幸運にも、一度この問題が発見されると、修正は簡単でした。もし詳細内容に興味があれば、対応するプルリクエストをチェックしてください。

8277212: GC accidentally cleans valid megamorphic vtable inline caches
https://github.com/openjdk/jdk/commit/976c2bb05611cdc7b11b0918aaf50ff693507aae
8277212: GC accidentally cleans valid megamorphic vtable inline caches
https://github.com/openjdk/jdk/pull/6450

この修正はJDK 17.0.2にもバックポートされています。

Linux/PowerPC Support

2013年、つまりZGCが誕生する前に、Linux/PowerPC(およびAIX/PowerPC)サポートをOpenJDKに導入するため、JEP 175が作成されました。

JEP 175: PowerPC/AIX Port
https://openjdk.java.net/jeps/175

最初の移植版はJDK 8u20で出荷され、それ以降もメンテナンスが続けられています。このプラットフォームをサポートするための努力は、当初からSAPの友人たちによって資金提供されています。

ですから、Linux/PowerPCでZGCを利用できるようにするためのパッチを提供したのもSAPであると聞いたとしても、驚かないかもしれませんね。新しいCPUアーキテクチャのサポートは、インタープリタと2つのJITコンパイラにZGCの様々なバリア(ロードバリア、methodエントリバリア、スタックウォーターマークバリア)を実装することが主な内容となっています。このパッチのコード量は約1200行です。

8274851: [PPC64] Port zgc to linux on ppc64le
https://github.com/openjdk/jdk/commit/337b73a459ba24aa529b7b097617434be1d0030e

JDK 18では、ZGCは以下のプラットフォームで動作します。

  • Linux/x64
  • Linux/AArch64
  • Linux/PowerPC
  • macOS/x64
  • macOS/AArch64
  • Windows/x64
  • Windows/AArch64

詳細は以下のリンクをどうぞ。

Supported Platforms
https://wiki.openjdk.java.net/display/zgc#Main-SupportedPlatforms

Summary

  • -XX:+UseStringDeduplication というJVMオプションがサポートされました。ヒープメモリの使用量を減らすために、Stringオブジェクトを支える同一の文字配列を検索し、重複排除するようにZGCに指示します。なお、この機能はデフォルトで無効になっています。
  • クラスのアンロードに非常に長い時間がかかるという10年来のバグが修正されました。
  • SAPの人たちのおかげで、ZGCがLinux/PowerPCで動くようになりました。

ZGCの詳細情報は、OpenJDK wiki、Inside JavaのGCの節、もしくはこのブログをご覧ください。

OpenJDK Wiki
https://wiki.openjdk.java.net/display/zgc/Main
Inside Java
https://inside.java/tag/gc
perliden
https://malloc.se/

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください