原文はこちら。
The original article was written by Alina Yurenko (Developer Advocate for GraalVM, Oracle Labs).
https://medium.com/graalvm/graalvm-21-3-is-here-java-17-native-image-performance-updates-and-more-ac4cbafcfc05
本日GraalVM 21.3をリリースしました。GraalVM 21.3にはコミュニティから長らく待望されていた数多くの素晴らしい機能が含まれています。このエントリでは最も興味深い、期待されているものについて説明していきます。
Get Updated
21.3はその年の最後のリリースであり、GraalVM Community Editionでは今後12ヶ月間アップデートを受け続けることになります。GraalVMのアップグレードを検討されている方は、ぜひこの機会にアップグレードをご検討ください。
Download GraalVM 21.3 Community
https://www.graalvm.org/downloads/
Download 21.3 Enterprise
https://www.oracle.com/downloads/graalvm-downloads.html
JDK11ベースのビルドとともに、GraalVM 21.3では最近リリースされたJava 17ベースのビルドも利用いただけます。これはつまり、switch式でのパターンマッチングやシールクラス(sealed class)、プラットフォームのアップデートなど、あらゆるすばらしいJava 17の機能に加えて、Java 17に蓄積されたJava 11以後の機能も利用できる、ということです。是非お試しください。GraalVMとMicronautでJava 17の機能を最大限に活用する方法については、Graeme RocherによるDeveloper Liveのセッションをご覧ください。
今回のリリースでは、新たなEnterpriseコンテナーイメージとCommunityコンテナーイメージのセットを用意しました。Oracle Container RegistryからEnterpriseコンテナーイメージを入手するには、以下のコマンドを使います(なおOCRへのログイン後、docker login
ならびにライセンス承諾する必要があります)。
docker pull container-registry.oracle.com/graalvm/enterprise-8:jre-headless
Communityコンテナーイメージをご所望なら、GitHubから入手できます。
docker pull ghcr.io/graalvm/graalvm-ce:latest
VS CodeのGraalVM Tools for Java拡張を使ってGraalVMの最新版をインストールすることもできます。
GraalVM Tools for Java
https://marketplace.visualstudio.com/items?itemName=oracle-labs-graalvm.graalvm
Compiler Updates
毎回のリリースと同様に、Graal コンパイラのアップデートでは、新たなパフォーマンスの最適化が行われ、以前の実験的な機能が昇格しています。例えば、カウントされていないループに対する最適化Strip Miningがデフォルトで有効になりました。この最適化により、より多くのカウントされていないループがカウントループに変換され、ベクトル化やパーシャルアンローリングなどのさらなる最適化の対象となります。これにより、カウントされないループを多用するワークロードでは、最大15%のスピードアップが実現します。
Enhanced Automated Vectorizationもデフォルトで有効になっています。これにより、数学を多用する機械学習ワークロードのような特定のワークロードでGraalVM EnterpriseはOpenJDKよりも最大で40%高速化します。
Enhanced Automated Vectorization
https://medium.com/graalvm/enhanced-automated-vectorization-in-graalvm-76cd99925b6d
https://logico-jp.io/2021/08/22/enhanced-automated-vectorization/
One of the new optimizations added in 21.3で追加された新たな最適化の一つが、実現不可能なパスを排除することを目的としたInfeasible Path Correlationという最適化です。
以下の例を見てください。
class A { ... }
class B extends A { ... }
if (x > 5) {
if (y instanceof A) {
throw Error();
}
}
loop {
int foo = <expensive computation>;
if (x > 10) {
if (y instanceof B) {
bar(foo);
}
}
}
この例で、 bar(foo)
の呼び出しが行われるのは、 x > 10 && y instanceof B
の場合のみです。しかしながら、この条件は x > 5 && y instanceof A
を意味しているため、 throw Error()
が呼び出されます。つまり、 bar
の呼び出しが実際には行われない、ということです。Graalではこの点を認識し、bar
の引数としてのみ foo
が使用されるのみの場合、bar
の呼び出しと foo
の計算の両方を排除するようになりました。
この最適化はGraalVM Enterpriseでデフォルトで有効になっていますが、 -Dgraal.InfeasiblePathCorrelation=false
で無効化できます。
さらに、GraalVM Enterpriseは、JITスプレー攻撃を防御するためのConstant Blindingのサポートを追加しています。constant blindingフェーズは、コード内のユーザーが提供した定数をランダムに生成されたキーで暗号化し、攻撃者が実行メモリ内の即値の存在に依存できないようにします。Constant Blindingは実験的な機能であり、デフォルトでは無効になっていますが、 -Dgraal.BlindConstants=true
で有効化できます。
今回のGraalVMのリリースでは、Javaプログラムのプロファイルの取得と分析を改善するための新しいツール proftool
も導入されました。
proftoolのREADME
https://github.com/graalvm/mx/blob/master/README-proftool.md
このツールは、JITパフォーマンス分析を支援するために、実行に関するマシンレベルの詳細を提供することを目的としています。現時点では、JVMによって生成されたすべてのアセンブリをキャプチャするJVMTIエージェント、Linuxのperf出力のパーサー、HotSpot LogCompilation情報のパーサーが含まれています。これらのコンポーネントを1つのコマンドラインにまとめることで、perfプロファイル情報をJITコードに帰属させることができます。プロファイルの収集は現在LinuxのPerfのみをサポートしていますが、一度データを取得すれば、そのプロファイルをどこでも見ることができます。このツールについては、後続のエントリで詳しく説明する予定です。
Native Image
21.3では、Native Imageの条件付きリフレクション構成(conditional reflection configuration)が導入されました。イメージのビルド時に必要となるReflection、JNI、クラスパスリソース、Dynamic Proxyオブジェクトの設定ファイルエントリが、クラスの到達可能性に基づいて条件付きで設定できるようになりました。条件付き構成では、提供された条件を満たす場合にのみ、クラス構成エントリが適用されます。現在サポートされている条件は typeReachable
のみで、これは指定されたタイプが他のコードで到達可能な場合に構成エントリを有効にするものです。たとえば、io.netty.util.internal.PlatformDependent0
に到達可能な場合にのみ sun.misc.Unsafe.theUnsafe
へのリフレクティブ・アクセスをサポートするには、次のように構成します。
{
"condition" : {
"typeReachable" : "io.netty.util.internal.PlatformDependent0"
},
"name" : "sun.misc.Unsafe",
"fields" : [
{ "name" : "theUnsafe" }
]
}
このおかげでより正確な設定が可能となり、ネイティブ実行ファイルのサイズを小さくできます。条件付きリフレクションについては、この後のブログで詳しくご紹介します。
Native Imageに関するもう一つのアップデートは特にコミュニティから待望されていたものです。最近の調査によると、開発者がNative Imageで改善してほしいことの第1位はビルド時間でした。
21.3では、過去数回のリリースにおけるビルド時間とメモリ使用量の改善に向けた一貫した取り組みが反映されています。実際21.2でも、ビルド時間が20%短縮され、メモリ使用量が9%程度削減されたとのコミュニティからの改善報告がありました。
21.3では、これらの改善がさらに進んでいます。私たちは、Spring petclinic-jdbcのイメージサイズとイメージビルド時間の変化を測定しました。
Spring PetClinic Sample Application built with Spring Data JDBC
https://github.com/spring-petclinic/spring-petclinic-data-jdbc

Spring Nativeの進捗状況に興味のある方は、Spring OneでのSébastien Deleuze氏の最近の講演をご覧ください。最新の機能、パフォーマンスの数値、GAの計画などが紹介されています。
また、Native Imageでは、静的解析の精度を上げるために、小さなメソッドが静的解析の前にインライン化されるようになりました。これにより、定数の折り返しが改善され、驚きが減ります。例えば、以前はstatic final
フィールドに直接アクセスするコードと、アクセサメソッド経由でstatic final
フィールドにアクセスするコードでは、最適化の仕方が異なりました。
void foo() {
if (MyConfiguration.WINDOWS) {
// The static analysis does not see this code as reachable on non-Windows platforms.}}
}
}
static boolean isWindows() {
return MyConfiguration.WINDOWS;
}
void bar() {
if (isWindows()) {
// The static analysis marked this code as reachable because the method invocation prevented constant folding before the static analysis. With method inlining before static analysis enabled now, this code is no longer seen as reachable on non-Windows platforms.(静的解析では、静的解析前のメソッド呼び出しで定数折り畳みができなかったため、このコードは到達可能と判定されました。静的解析前のメソッドのインライン化が有効になったため、このコードは Windows 以外のプラットフォームでは到達可能とみなされなくなりました)
}
}
21.3では、リフレクションのメタデータの保存方法も変更されました。Native Imageは、クエリされたリフレクションメソッドと呼び出されたリフレクションメソッドを区別するようになりました。この区別により、イメージサイズがさらに小さくなります。例えば、webmvc-tomcatでは、イメージサイズが9%減少しています。
Spring Boot project with Spring MVC, Tomcat and Jackson.
https://github.com/spring-projects-experimental/spring-native/tree/main/samples/webmvc-tomcat
メタデータの問い合わせにのみ使用するメソッドをマークするためには、以下の例のようにreflection-configuration
エントリの前にquery
を付ける必要があります。
{
"name": "org.graalvm.Example",
"queryAllDeclaredConstrctors": true,
"queriedMethods": [{"name":"queriedOnlyMethod", "parameterTypes":[]}]
}
また、Native Imageエージェントは、クエリされたメソッドと呼び出されたメソッドを区別して、適切な設定を出力します。
21.3のもう一つの大きなアップデートは、Javaプラットフォームのモジュールシステムの初期サポートです。Native Imageジェネレータは、-m
(または--module
)、-p
(または--module-path
)、--add-exports
、--add-opens
といった、Javaランチャーで知られているモジュール関連のオプションを受け付けるようになりました。-m
や-p
のようなモジュール関連の引数が使われると、イメージ・ジェネレーター自体がJavaモジュールのセットとして実行されます。今回の変更は、イメージ・ジェネレータの動作を大きく変えるものであり、モジュール・システムのより多くの側面をサポートするために、現在も開発が続けられています。この新機能をお試しになった際には、フィードバックやバグレポートをお願いします。
例えば、base-module.jar
とapp-module.jar
の2つのJavaモジュールからイメージを作成できます。モジュールsample.app
にはメインクラスが含まれています。
native-image --module-path base-module.jar:app-module.jar --module sample.app
21.3では、GraalVM Enterprise Native Imageのピークパフォーマンスが大幅に改善されました。プロファイルに基づく最適化(profile-guided optimizations、PGO)とG1により、ネイティブ実行ファイルは起動時のパフォーマンスとリソース使用量に加えて、OpenJDKと同等のピーク・パフォーマンスを達成しています。
Profile-Guided Optimizations
https://docs.oracle.com/en/graalvm/enterprise/21/docs/reference-manual/native-image/PGO/
プロファイルに基づく最適化
https://docs.oracle.com/cd/F44923_01/enterprise/21/docs/reference-manual/native-image/PGO/
Memory Management at Image Run Time
https://docs.oracle.com/en/graalvm/enterprise/21/docs/reference-manual/native-image/MemoryManagement/
イメージの実行時におけるメモリー管理
https://docs.oracle.com/cd/F44923_01/enterprise/21/docs/reference-manual/native-image/MemoryManagement/

ネイティブイメージのパフォーマンスに関連するもう一つの素晴らしいアップデートは、アプリケーションのランタイムメモリーフットプリントを削減するためのシリアルGCの新しいポリシーです。この新しいポリシーは、以前からコードベースに存在していたものの有効化されていなかった機能で、Young generationのためのsurvivorスペースを有効にします。さらに、full GCでは、ルートポインタのためにイメージヒープ全体をスキャンすることはなくなり、書き込まれたイメージヒープの一部のみがスキャンされるようになりました。この新しいポリシーはデフォルトでは有効になっていませんが(これは次のリリースでデフォルトで有効にする予定です)、 -H:InitialCollectionPolicy=Adaptive
で有効にできます。新しいポリシーをテストして、何か問題があれば報告してください。
本リリースのNative Imageは、Java 17にも対応しています。他の機能とともに、Red HatのコントリビュートによるJDK 17用の JDK Flight Recorder (JFR) も含まれています。
Polyglot Runtime and Embedding
今回のリリースでは、内蔵のCPUサンプラーツールの使い勝手と精度を大幅に向上させました。CPUサンプラーは、すべてのポリグロット言語で動作し、すべてのランチャーに内蔵されたコマンドラインツールとして提供されます。特に、以下の点が改善されました。
- 新しいゲスト言語のセーフポイント機構を使用することで、サンプリング出力の精度が向上しました。サンプリング出力では、デフォルトでコンパイル単位に加えてインライン・メソッドも表示されるようになりました。
- デフォルトのサンプリング・ヒストグラムの出力が簡素化され、各最適化層での時間を表示する
--cpusampler.ShowTiers
オプションが追加されました。 - CPUSamplerにSVGフレームグラフの出力形式を追加しました。これを有効にするには、新しい
--cpusampler.Output=flamegraph
オプションを使用します。

このflamegraphの例は、以下のコマンドを実行して作成したものです。
js --cpusampler --cpusampler.Output=flamegraph --cpusampler.OutputFile=out.svg primes.js
GraalVM Enterpriseには、Auxiliary Engine Caching(補助エンジンキャッシング)と呼ばれるコード・スナップショット機能の安定性とパフォーマンスの向上が含まれています。Engine Cachingは、Truffleのゲスト言語プログラムの(ロード、パーシング、プロファイリング、コンパイルなどの操作に起因する)ウォーミングアップを解消することを目的としています。1つのOSプロセス内では、ウォームアップ中に行われる作業をプロセス内エンジンキャッシングで共有できます。Auxiliary Engine Cachingは、このメカニズムをベースに、ASTと最適化されたマシンコードを含むコードスナップショットをディスクに保存する機能を追加したものです。これにより、ゲストアプリケーションの最初の実行でも、ウォームアップを大幅に削減できます。この機能を実現するために、アプリケーションを変更する必要はありません。この機能の詳細については、近日中にブログ記事でご紹介します。
今回のアップデートでは、polyglot 組み込み開発者向けに、コンテクスト間で値を共有する機能が追加されました。これまで値の共有は、プリミティブとホスト値に対してのみサポートされていましたが、ゲスト値に対してはエラーが発生していました。この制限は今回のリリースで解除されました。値の共有は、public Context.Builder allowValueSharing(boolean enabled)
で完全に無効にすることもできます。厳密な安全性が求められ、あるコンテキストの値が別のコンテキストに渡されるとエラーになってしまうような場合には、共有をオフにすると便利です。この機能は長年の課題であったGitHub Issue #631を解決するもので、すでにコミュニティから暖かく迎えられています。
Sharing / Re-using values across Contexts #631
https://github.com/oracle/graal/issues/631
厳密な安全性が求められるワークロードのために 21.3 に搭載されたもう一つの機能が、ゲストからホストへのコールバックに対する値のスコーピング(value scoping)です。値のスコーピングが設定されたゲストアプリケーションから呼び出されたホストメソッドは、メソッドが戻る際に渡された値を自動的に解放します。これにより、組み込み開発者は、値がホストメソッド呼び出しのライフタイムを超えないようにすることができます。メソッドが返された後にスコープ対象の値にアクセスすると、エラーが発生します。値のスコープ設定は、コンテキストの構築時に HostAccess.SCOPED
プリセットを使用して有効にできます。
今回のリリースでは、言語実装者向けに、静的オブジェクトを実装するための新しいAPIが追加されました。
Package com.oracle.truffle.api.staticobject
https://www.graalvm.org/truffle/javadoc/com/oracle/truffle/api/staticobject/package-summary.html
新しいStatic Object Model APIは、一度定義すると、プロパティの数やタイプが変わらないオブジェクトのレイアウトを表現できます。特に、静的プログラミング言語のオブジェクトモデルの実装に適していますが、これに限定されるものではありません。また、今回のリリースでは、すべてのプラットフォームにおいて、言語コンテキストの検索が大幅に高速化されています。
JavaScript
21.3では、Node.jsのバージョンが14.17.6に更新され、新機能とセキュリティ修正が含まれています。通常通り、Error Cause、Import Assertions、Accessible Object.hasOwnPropertyなど、いくつかの新しいECMAScriptの提案を実装しました。これらを試すには、対応するフラグ(例:--js.error-cause
、--js.import-assertions
)を使用してください。また、GraalVMのWebAssemblyサポートを改善し、JavaScriptのBigIntからWebAssemblyのi64への統合を含む、いくつかの完全性とパフォーマンスの修正を行いました。
WebAssembly BigInt<->i64 conversion in JS API
https://github.com/WebAssembly/JS-BigInt-integration
Python
21.3におけるPythonの主な改善点は、HPyネイティブ拡張のバイナリサポートが追加されたことで、これにより、CPythonと同じHPyバイナリパッケージを再コンパイルせずに実行できるようになりました。
もう1つの注目すべき改善点は、マルチプロセシングモジュールのサポートで、マルチコアマシン上の複数のPythonコンテキストにまたがるタスクのオーケストレーションをサポートしています。例えば、文献のコーパス全体のペアワイズJaccard係数(Jaccard similarity indices)を計算する場合、すべてのコアを利用するようにスケールアップできます。4つのワーカーを備えたマルチプロセシングプールを使用すれば、このワークロードのパフォーマンスは3倍になります。この機能はコミュニティからの要望が高かったため、今後のフィードバックを期待しています。
Python のもう一つの改善点は、ネイティブコードを使用して書き直されたソケットモジュールで、以前よりもはるかに幅広いネットワークユースケースが動作するようになりました。これには、unix ドメインソケットを使用したメッセージ指向のソケットコール (recvmsg
/sendmsg
) や、Python からネイティブ拡張を使用して開いたソケットの適切なインタラクションが含まれます。また、 ctypes モジュールのサポートも追加され、 ctypes API を使用するより多くのネイティブエクステンションが実行できるようになりました。例えば、PythonからImageMagickに接続するためのWandパッケージは、GraalVM 21.3で動作します。
Wand : ctypes-based simple ImageMagick binding for Python
https://github.com/emcconville/wand
Ruby
正規表現を多用するワークロードを高速化するために、TruffleRubyではTRegexを採用しました。TRegexは、GraalVMコンパイラとTruffle APIを使用して、効率的に正規表現を実行する、汎用の正規表現エンジンです。TRegexは、ECMAScriptの正規表現とPythonの正規表現のサブセットの実装を提供していますが、今回、Rubyにも対応しました。TRegex の特徴は、正規表現を有限状態オートマトンにコンパイルすることです。これは、マッチを検索する際のパフォーマンスが予測可能であることを意味します(入力のサイズに対して線形)。
TRegexをデフォルトで使用することで、TruffleRubyは正規表現のマッチングを大幅に高速化できます。TRegexを使ったTruffleRubyでは、大きな正規表現のベンチマークで最大9倍の速度が出ていることがわかります。


TRegexがどのように動作しているかについては、Benoit Daloze氏とJosef Haider氏によるRubyKaigiセッションをご覧ください。
その他の改良点として、TruffleRubyでは、Polyglotモードでの外部オブジェクトのtrait(配列、ハッシュ、イテレートなど)が完全に統合され、Rubyと同じように動作するようになりました。また、ExecJS gemで使用されている透過的インナーコンテキスト(transparent inner contexts)もサポートされています。
FastR
GraalVMのR実装であるFastRでは、引き続き互換性を重視しています。特に、PCREからPCRE2バージョン10.37へのアップグレードが行われました。
PCRE – Perl Compatible Regular Expressions
https://www.pcre.org/
この変更がどのような意味を持つかについては、GNU-R変更ログのMigration to PCRE2(PCRE2への移行)の章を確認してください。
R News
https://cran.r-project.org/doc/manuals/r-devel/NEWS.html
また、Mapsなどのいくつかのパッケージ のコンパイル時の設定を改善し、さらにいくつかの使い勝手の向上とバグの修正を行いました。
Java on Truffle
本リリースの Java on Truffle では、ゲストとホストの両方で Java 17 をサポートしています。また、起動時のパフォーマンスも大幅に改善されています。例として、いくつかの有名なベンチマークの最初の反復にかかる時間を 21.0.0 と 21.3.0 で比較してみます(最初の反復時間を測定しているため、低い方が良い結果です)。

その他の変更点については、リリースノートやプロジェクトの変更履歴をご覧ください。
GraalVM Release Notes
https://www.graalvm.org/release-notes/21_3/
Espresso Changelog
https://github.com/oracle/graal/blob/master/espresso/CHANGELOG.md#version-2130
WebAssembly
過去数回のリリースでは、GraalVMのWebAssemblyランタイムのパフォーマンス改善に取り組んできましたが、その結果、注目すべき変化がありました。

さらに、JavaScriptとWebAssemblyの間のインタラクションは、WASM Embedding Interfaceに基づいて行われるようになりました。その他の変更点については、リリースノートやプロジェクトの変更履歴をご覧ください。
GraalVM Release Notes
https://www.graalvm.org/release-notes/21_3/
Wasm (GraalWasm (GraalVM Implementation of WebAssembly) Changelog
https://github.com/oracle/graal/blob/master/wasm/CHANGELOG.md
LLVM Runtime
C/C++をコンパイルするLLVMツールチェーンがバージョン12.0.1に更新されました。
LLVM Toolchain for Compiling C/C++
https://www.graalvm.org/reference-manual/llvm/Compiling/#llvm-toolchain-for-compiling-cc
また、コードベースを「マネージドモード」と「ネイティブモード」のコードでモジュール化しました。
Limitations and Differences to Managed Execution on Top of GraalVM Enterprise
https://www.graalvm.org/reference-manual/llvm/NativeExecution/#limitations-and-differences-to-managed-execution-on-top-of-graalvm-enterprise
これにより、マネージドモードとネイティブモードの間の静的なビルド依存性が軽減されると同時に、マネージドモードのコードベースは、管理されていないメモリアクセスをすべて削除することで、より堅牢になりました。
Tools
本リリースでは、GraalVM Tools for Micronaut ExtensionにKubernetesのサポートが追加されました。
GraalVM Tools for Micronaut Extension
https://www.graalvm.org/tools/vscode/micronaut-extension/
これにより、Kubernetesクラスタ内のMicronautアプリケーションのデプロイ、実行、デバッグをVisual Studio Codeから直接行うことができます。Micronaut用の以下のクイックアクションが有効になっています。

Micronaut: Deploy to Kubernetesアクションを呼び出すと、Javaプロジェクトをパッケージ化し、Dockerイメージをビルドして、コンテナレジストリにプッシュします。DeploymentをKubernetesクラスタ内に作成し、ローカルポートはPod内で動作するアプリケーションに転送されます。
デプロイ後、VSCode GraalVM Extension Pack内からこのプロジェクトをデバッグできます。これを行うには、”Set Kubeconfig
“を使ってKubernetes extensionをクラスタに接続し、開発対象のノードを選択して、Debug (“Attach using Java 8+”) アクションを起動します。これにより、kubectl
ポートフォワード機能を使って、リモートのK8s Podのデバッグを行います。
さらに、GraalVM Tools for Java拡張機能で可能になったVS CodeでのNative Imageのデバッグが大幅に改善されました。ネイティブイメージのプロセスにデバッガーをアタッチして、イメージのリアルなコードをステップオーバーできるようになりました。デバッガーをネイティブの実行ファイルにアタッチするには、launch.json
に設定を追加します。
GraalVM Tools for Java
https://marketplace.visualstudio.com/items?itemName=oracle-labs-graalvm.graalvm
launch.json
ファイルの設定補完から“Native Image: Attach to Process”を選択します。これにより”Attach to Native Image”の構成を生成します。この構成を選択して実行すると、実行プロセスのリストが現れますので、ネイティブ実行ファイルに対応するものを選択してください。

GraalVM拡張はJava 17でも動作するようアップデートされました。
Conclusion
このリリースで行われたすべてのアップデートゆえに、GraalVMのバージョンのアップデートにはちょうどよい機会です。GraalVM EnterpriseとGraalVM Communityの両方の新しいビルドを入手できます。
Download GraalVM 21.3 Community
https://www.graalvm.org/downloads/
Download 21.3 Enterprise
https://www.oracle.com/downloads/graalvm-downloads.html
このリリースに寄せられたすべてのフィードバック、提案、貢献に対して、コミュニティのみなさまに感謝しております。このリリースに関する追加のフィードバックや、今後の機能に期待する機能の提案があれば、Slack、GitHub、Twitterでお知らせください。
GraalVM Slack Invitation
https://graalvm.org/slack-invitation
oracle/graal: GraalVM: Run Programs Faster Anywhere
https://github.com/oracle/graal
Twitter
https://twitter.com/graalvm