原文はこちら。
The original article was written by Alina Yurenko (Developer Advocate for GraalVM, Oracle Labs).
https://medium.com/graalvm/graalvm-22-0-is-here-c7acc82a8c2e
本日GraalVM 22.0がリリースされ、新機能や改善がすべてのコンポーネントに渡ってもたらされています。このエントリではそのハイライトをお伝えします。
Get Updated
JDK 11およびJDK 17のGraalVM 22.0がリリースされました。いつも通り、GraalVM CommunityはGitHubから、GraalVM EnterpriseはOTNからダウンロードできます。
graalvm-ce-builds
https://github.com/graalvm/graalvm-ce-builds/releases
Oracle GraalVM Enterprise Edition
https://www.oracle.com/downloads/graalvm-downloads.html
VS CodeのGraalVM Extension Pack for Javaや、macOSのHomebrewを使っても最新版のGraalVMをインストールできます。
GraalVM Extension Pack for Java
https://marketplace.visualstudio.com/items?itemName=oracle-labs-graalvm.graalvm-pack
Homebrew Tap for GraalVM
https://github.com/graalvm/homebrew-tap
GraalVM 22.0リリース発表の動画は以下からご覧いただけます。この中で新機能やデモを紹介しています。
では中身をご紹介します。
Native Image
Native Imageでは、開発者エクスペリエンスを向上させることを重要なテーマの1つとしています。過去数回のリリースで、画像のビルド時間やメモリ使用量を大幅に削減しました。
21.3でのアップデート
https://medium.com/graalvm/graalvm-21-3-is-here-java-17-native-image-performance-updates-and-more-ac4cbafcfc05#d243
https://logico-jp.io/2021/10/23/graalvm-21-3-is-here-java-17-native-image-performance-updates-and-more/#d243
この作業は22.0でも継続しており、イメージサイズを縮小するためのアップデートが加えられています。スタックフレームメタデータの圧縮エンコーディングにより、あらゆるイメージのイメージサイズが小さくなりました。GraalVM Enterpriseでは、String.format()
の最適化された実装によってこの点がさらに改善され、「Hello World」のような小さなイメージではlocalizationクラスが到達できなくなり、イメージサイズが大幅に削減されました。
本リリースにおけるもう一つの関連する更新は、Native Imageの新しいビルド出力です。
Native Image Build Output
https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/BuildOutput.md
これは、ビルドプロセスを初期化、解析の実行などの段階に分け、現在の段階を可視化し、コードやヒープの内訳、到達可能性などの統計、ビルドやRSS、CPU使用率などの側面を出力します。以下はその一例です。
================================================================================ | |
GraalVM Native Image: Generating 'helloworld'... | |
================================================================================ | |
[1/7] Initializing... (2.5s @ 0.21GB) | |
Version info: 'GraalVM dev Java 11 CE' | |
[2/7] Performing analysis... [*******] (5.6s @ 0.46GB) | |
2,565 (82.61%) of 3,105 classes reachable | |
3,216 (60.42%) of 5,323 fields reachable | |
11,652 (72.44%) of 16,086 methods reachable | |
27 classes, 0 fields, and 135 methods registered for reflection | |
57 classes, 59 fields, and 51 methods registered for JNI access | |
[3/7] Building universe... (0.5s @ 0.61GB) | |
[4/7] Parsing methods... [*] (0.5s @ 0.86GB) | |
[5/7] Inlining methods... [****] (0.5s @ 0.73GB) | |
[6/7] Compiling methods... [**] (3.7s @ 2.38GB) | |
[7/7] Creating image... (2.1s @ 1.04GB) | |
3.69MB (27.19%) for code area: 6,955 compilation units | |
5.86MB (43.18%) for image heap: 1,545 classes and 80,528 objects | |
3.05MB (22.46%) for debug info generated in 1.0s | |
997.25KB ( 7.18%) for other data | |
13.57MB in total | |
-------------------------------------------------------------------------------- | |
Top 10 packages in code area: Top 10 object types in image heap: | |
606.23KB java.util 1.64MB byte[] for general heap data | |
282.34KB java.lang 715.56KB java.lang.String | |
222.47KB java.util.regex 549.46KB java.lang.Class | |
219.55KB java.text 451.79KB byte[] for java.lang.String | |
193.17KB com.oracle.svm.jni 363.23KB java.util.HashMap$Node | |
149.80KB java.util.concurrent 192.00KB java.util.HashMap$Node[] | |
118.07KB java.math 139.83KB java.lang.String[] | |
103.60KB com.oracle.svm.core.reflect 139.04KB char[] | |
97.83KB sun.text.normalizer 130.59KB j.u.c.ConcurrentHashMap$Node | |
88.78KB c.oracle.svm.core.genscavenge 103.92KB s.u.l.LocaleObjec~e$CacheEntry | |
... 111 additional packages ... 723 additional object types | |
(use GraalVM Dashboard to see all) | |
-------------------------------------------------------------------------------- | |
0.9s (5.6% of total time) in 17 GCs | Peak RSS: 3.22GB | CPU load: 10.87 | |
-------------------------------------------------------------------------------- | |
Produced artifacts: | |
/home/janedoe/helloworld/helloworld (executable) | |
/home/janedoe/helloworld/sources (debug_info) | |
/home/janedoe/helloworld/helloworld (debug_info) | |
/home/janedoe/helloworld/helloworld.build_artifacts.txt | |
================================================================================ | |
Finished generating 'helloworld' in 16.2s. |
この機能はデフォルトで有効になっていますので、ぜひ試してフィードバックしてください。以前のビルド出力に依存している場合は、-H:-BuildOutputUseNewStyle
を使用すれば切り替えできます。以前の出力スタイルはdeprecated(廃止対象のため非推奨化)で、将来のリリースで削除される予定であることに注意してください。
もうひとつの更新は、ネイティブ実行ファイルのメモリ要件に関連するものです。21.3 で導入されたSerial GCの新しいGCポリシーがデフォルトで有効になっています。
Serial GCの新しいGCポリシー
https://medium.com/graalvm/graalvm-21-3-is-here-java-17-native-image-performance-updates-and-more-ac4cbafcfc05#52a9
https://logico-jp.io/2021/10/23/graalvm-21-3-is-here-java-17-native-image-performance-updates-and-more/#52a9
これにより、ネイティブ実行ファイルの最大RSSサイズを最大30%削減できます。Spring Petclinicのような典型的なマイクロサービスのワークロードでは、最大23.22%のピークスループットの改善を測定しました。
21.3で導入され、このリリースで拡張されたもう一つの機能は、Java Platform Module Systemのサポートです。特にNative Imageでは、--add-reads
と--add-modules
オプションがサポートするようになりました。また、--add-reads
、--add-exports
、--add-opens
などのモジュール関連のオプションはすべて、クラスパスやモジュールパスのスキャンの前に適用されるようになりました。これにより、クラス読み込み前にモジュールが適切に設定されるようになり、クラス読み込みエラーを回避できます。また、モジュールに関するより多くの情報がイメージヒープに追加され、実行時にモジュールのイントロスペクションがより可能になりました。
GraalVM Native Build Toolsには、過去数回のリリースでネイティブエージェントとの統合の改善などの改善が加わっています。最新のリリースはGitHubから入手できます。
Native Build Tools
https://graalvm.github.io/native-build-tools/latest/index.html
リリース情報
https://github.com/graalvm/native-build-tools/releases
最新のリリースと機能といえば、JDK 17のsealedクラスのreflectiveイントロスペクションのサポート(Class.isSealed()
と Class.getPermittedSubclasses()
)を追加しました。このリリースの一環で、JSONファイルを使った置換登録のためのオプション -H:SubstitutionFiles=...
が削除されていますのでご注意ください。
Java and Compiler Updates
このリリースでは、GraalVM Enterprise コンパイラがプロファイリング情報を扱う方法を大幅に変更しました。GraalVMコンパイラは、高度に最適化されたJITコンパイラとして設計されており、基礎となるランタイム(HotSpot VMまたはNative Image)によって収集されたプロファイリング情報に大きく依存しています。コンパイラはプロファイルを使い、
- どのコードの分岐が最も頻繁に実行されているか
- ループの実行頻度
- ポリモーフィックコードでどの型が使用されているか
を特定し、最適化に注力する場所を決定します。したがって、プロファイルの品質は、インライン化、複製、ベクトル化など、多くの最適化にとって非常に重要です。今回のリリースでは、プロファイルがない場合でも主要な最適化が合理的に行われるようにするため、コンパイラの AOT (ahead-of-time) モードへの自動切り替えをサポートします。これは、まだホットとなり得る一般的でないパターンをプロファイルしないTruffle言語や、プロファイルによる最適化を行わないNative Imageに有効です。この最適化により、GraalVM Enterpriseでは、優れたプロファイルを持たないループや型チェックの多いベンチマークで、最大25%の性能向上が確認されています。この最適化は、コンパイラの中核であるため、常に有効であり、無効にはできません。この最適化はユーザーに対して透過的であり、正確な分岐やループのプロファイルがない場合でも、一般的により良いコードを提供するはずです。
新たなループローテーション最適化により、より多くのuncounted loop(カウントしないループ)をcounted loop(カウントするループ)に変換し、ベクトル化やpartial unrollingなどの最適化の恩恵を受けられるようになりました。似たような形状のカウントしないループを多く含むワークロードでは、このループローテーション最適化により、最大30%の性能向上が得られます。この最適化は、GraalVM Enterpriseで利用可能で、22.0 ではデフォルトで無効になっています。これを有効にするには、
-Dgraal.LoopRotation=true
を使用します。我々は、将来のリリースでデフォルトで有効にすることを計画しています。
Community版とEnterprise版の両方で、Native Imageでの型切り替え(すなわち一連のinstanceof
ブランチ)のパフォーマンス向上のための新たな最適化が追加されました。たとえば、次のようなコードを考えてみましょう。
void foo(Object o) {
if (o instanceof A) {
// ...
} else if (o instanceof B) {
// ...
} else if (o instanceof C) {
// ...
}
}
Native Imageでは、以前のこのコードのコンパイラグラフは以下のようなものでした。
void foo(Object o) {
if (o != null && A.class.isAssignableFrom(o.getClass())) {
// ...
} else if (o != null && B.class.isAssignableFrom(o.getClass())) {
// ...
} else if (o != null && C.class.isAssignableFrom(o.getClass())) {
// ...
}
}
この新たな最適化により、o
のクラスのnullチェックとロードは分解されます。
void foo(Object o) {
if (o != null) {
Object nonNullO = o;
Class oClass = nonNullO.getClass();
if (A.class.isAssignableFrom(oClass)) {
// ...
} else if (B.class.isAssignableFrom(oClass) {
// ...
} else if (C.class.isAssignableFrom(oClass)) {
// ...
}
}
}
注意点としては、この最適化は、Native Imageが割り当てられたとみなすA
、B
、C
のサブクラスが存在する場合のみ効果があることです。そうでなければ、instanceof
テストはo
のクラスに関する==
を使った比較に変わります。
GitHub Action for GraalVM
先日、GraalVMの公式GitHub Actionにおけるアクションをリリースしました。
GitHub Action for GraalVM
https://github.com/marketplace/actions/github-action-for-graalvm
これを使うとGitHub ActionsのワークフローにGraalVM Community Edition、Native Image、Truffle言語とツールを簡単にセットアップして使用できます。このアクションに対するフィードバックを提供し、Twitterで興奮を分かち合ってくれたコミュニティの皆さんに感謝します。
RFC: GitHub Action for GraalVM (GR-33015)
https://github.com/oracle/graal/discussions/4150
この新たなアクションは、$GRAALVM_HOME
をエクスポートし、それにあわせて$JAVA_HOME
を設定するため、アプリのビルド、テスト、およびデプロイにGraalVMを使用できます。またこのアクションは、$PATH
を拡張するので、Truffle言語やツールを直接呼び出すことができるとともに、選択したコンポーネントに応じてビルド環境をセットアップします。それゆえ、例えば、追加のステップを必要とせずネイティブイメージを生成できます。以下は、ワークフローでの新しいアクションの使用方法についての例です。
steps: | |
- uses: actions/checkout@v2 | |
- uses: graalvm/setup-graalvm@v1 | |
with: | |
version: '22.0.0.2' | |
java-version: '17' | |
components: 'native-image' | |
github-token: ${{ secrets.GITHUB_TOKEN }} | |
- name: Example step | |
run: | | |
echo "GRAALVM_HOME: $GRAALVM_HOME" | |
echo "JAVA_HOME: $JAVA_HOME" | |
java --version | |
native-image --version |
追加のテンプレート、すべての機能とオプションのリスト、詳細情報は、GitHub MarketplaceのGitHub Action for GraalVMをチェックしてください。対応する setup-graalvm リポジトリにフィードバックや機能リクエストをお願いします。
GitHub Marketplace
https://github.com/marketplace/actions/github-action-for-graalvm
GitHub Action for GraalVM
https://github.com/graalvm/setup-graalvm
Polyglot Runtime and Embedding
GraalVM Enterpriseでpolyglot isolateが導入されました。これにより、他の多くの機能とともに、ホストとゲストアプリケーション間のヒープを分離できるようになりました。isolateを使用すると、Truffle言語のセキュリティ、起動、およびウォームアップ時間が向上します。
Context.Builder.option("engine.SpawnIsolate", "true")
を呼び出すことで、Context (Engine)
に対してisolateを作成できます。このモードでは、ホストとゲスト間の呼び出しはネイティブの境界を越える必要があるため、よりコストがかかります。ホストとゲスト間の強い循環参照を避けるために、このモードを使う場合はHostAccess.SCOPED
ポリシーの使用をお勧めします。なお、このモードはこのリリースでは実験的なものであり、JavaScriptのみサポートしている点にご注意ください。将来のリリースでサポート範囲を拡張する予定です。
また、ネイティブモードで、JavaScriptとNodeにてAuxiliary Engine Caching(補助エンジンキャッシュ)を有効にしました。
Auxiliary Engine Caching
https://github.com/oracle/graal/blob/master/truffle/docs/AuxiliaryEngineCachingEnterprise.md
Auxiliary Engine Cachingは、Truffle 言語上で動作するプログラムのウォームアップ(読み込み、解析、プロファイリング、コンパイルなどの操作に起因するもの)を解消することを目的としています。単一のOSプロセスの中で、ウォームアップ中に行われる作業をインプロセス・エンジン・キャッシングで共有できます。Auxiliary Engine Cachingは、この仕組みをベースにしつつ、ASTと最適化された機械語コードを用いてコード・スナップショットをディスクに永続化する機能を追加しています。このようにして、ゲストアプリケーションの最初の実行でもウォームアップを大幅に削減できます。
JavaScript
このリリースのJavaScriptのGraalVMランタイムでは、デフォルトでECMAScript 2022モードを有効にしました。Intl.DisplayNames v2
、Intl Locale Info
、Intl.DateTimeFormat.prototype.formatRange
、Extend TimeZoneName Option
、Intl Enumeration API
といったいくつかの新たなProposalを実装しました。これらは対応するフラグを使うと利用できます。また、Node.jsランタイムが14.18.1にアップデートされています。
Python
GraalVM Pythonランタイムの互換性とモジュールサポートの拡張に引き続き取り組んでいます。 このリリースでは、pyexpat
と_csv
モジュールのサポートを追加し、wheel
およびclick
PyPIパッケージとの互換性を向上させました。
Ruby
このリリースでは、Ruby 3.0 をサポートします。
Ruby 3.0 support
https://github.com/oracle/truffleruby/issues/2453
このリリースでは、Ractor、パーサの変更、キーワード引数の変更を除き、Ruby 3 の変更点のほとんどが実装されています。
また、インタプリタを高速化するためにいくつかの最適化を行い、コードがJITコンパイルされる前のアプリケーション性能を向上しました。つい最近、TruffleRubyと他のRuby実装のパフォーマンスを比較するブログ記事を公開しましたので、Rubyを使用している方はぜひご覧ください。
Benchmarking CRuby, MJIT, YJIT, JRuby, and TruffleRuby
https://medium.com/graalvm/benchmarking-cruby-mjit-yjit-jruby-and-truffleruby-6a7178ca6906
https://logico-jp.io/2022/01/21/benchmarking-cruby-mjit-yjit-jruby-and-truffleruby/
FastR
このリリースでは、互換性ならびにパッケージサポートに引き続き取り組んできました。また、TruffleのNodeLibraryを採用しました。これにより特定の Node ロケーションに関連するゲスト言語情報へのアクセスが可能になります。
NodeLibrary
https://www.graalvm.org/truffle/javadoc/com/oracle/truffle/api/interop/NodeLibrary.html
Java on Truffle
ちょうど一年前にJava on Truffleを発表しました。
Java on Truffle — Going Fully Metacircular
https://medium.com/graalvm/java-on-truffle-going-fully-metacircular-215531e3f840
https://logico-jp.io/2021/01/21/java-on-truffle-going-fully-metacircular/
それ以来、多くの新機能を追加したり、起動時間とピーク性能を改善したりして、コミュニティから多くの関心を寄せられるようになりました。
また、GraalVM LLVMランタイムでネイティブコードを実行するためのサポートも追加しました。
GraalVM LLVM Runtime
https://www.graalvm.org/22.0/reference-manual/llvm/
これは
--java.NativeBackend=nfi-llvm
という実験的フラグで有効にできます。ネイティブJDKライブラリは、
gu install espresso-llvm
でインストールできます。インストールすると、nfi-llvm
ネイティブバックエンドがこれらのライブラリをピックアップします。これにより、デフォルトのネイティブバックエンド(nfi-dlmopen
)のいくつかの制限を回避できます。特に、複数のコンテキストを使用したときにglibcのバージョンによって起こる可能性のあるクラッシュを回避できます。
また、このリリースでは、クラスの再定義機能を拡張しました。特に、フィールドとクラスアクセス修飾子の変更のサポートを追加しました。
WebAssembly
互換性およびパフォーマンスの取り組みとともに、WebAssemblyランタイムでは新たなTruffle Frame APIを採用しました。
Interface Frame
https://www.graalvm.org/truffle/javadoc/com/oracle/truffle/api/frame/Frame.html
LLVM Runtime
このリリースでは、いくつかの改良と修正が行われました。特に、ループカウントのプロファイルが第1層コンパイル済みコードでも報告されるようになり、ループの多いメソッドに対して第1層から第2層のコンパイルに早く移行することでウォームアップを改善しました。
Tools
このリリースでは、GraalVM Extension Pack for Java for VS Codeの開発者エクスペリエンスにいくつかの変更を加えました。
GraalVM Extension Pack for Java
https://marketplace.visualstudio.com/items?itemName=oracle-labs-graalvm.graalvm-pack
具体的には以下の通りです。
SDKManでGraalVMのインストールを管理する機能を追加
変更メソッドのシグネチャのリファクタリングにグラフィカルなUIを提供
GradleとMavenプロジェクトにProject Viewを追加
SDKMAN! the Software Development Kit Manager
https://sdkman.io/

プログラムの引数、VMオプション、環境変数など、Run Configurationパラメータをカスタマイズできるようになりました。 より高度なシナリオは、launch.json
を使い構成を設定できます。
また、VisualVMではJDK 18の予備的なサポートと、JFRイベントによる正確なスレッド状態監視を追加しました。
Add exact thread state monitoring
https://github.com/oracle/visualvm/issues/363

Truffle Language and Tool Implementations
本リリースにおける重要な更新は、共有レイヤーの導入です。共有レイヤーは、1つまたは複数のpolyglotコンテキスト内でコードを共有する一連の言語インスタンスです。以前のバージョンでは、新しい言語コンテキストが作成されるたびに、言語インスタンスが個別に共有されていました。本リリースではそれに代わり、レイヤー全体を共有できる場合に限り、言語インスタンスが新しいコンテキストで再利用されるようになりました。レイヤーを共有できるのは、そのレイヤーで初期化されたすべての言語が同じコンテキストポリシーをサポートし、そのオプションに互換性がある場合です。
以前は、異なるTruffle言語は終了するために異なるメカニズムを使用することがありました。これは、Truffle 言語が異なる言語がトリガーした終了を検出して処理する方法がないため、最適とは言えませんでした。22.0 では、TruffleはTruffleContext.closeExited(Node,int)
を使ってゲスト言語がトリガーするpolyglotコンテキストのhard explicit exitをサポートしています。これは、基礎となるpolyglotコンテキストの終了を言語がトリガーするための統一した方法を提供します。トリガーされると、初期化されたすべてのゲスト言語はまずTruffleLanguage.exitContext(C,ExitMode,int)
を使って通知され、次にすべてのコンテキストスレッドが停止され、そして最後にコンテキストをクローズします。hard explicit exitを単にhard exitと呼びます。
また、com.oracle.truffle.api.frame.Frame
とcom.oracle.truffle.api.frame.FrameDescriptor
に新しい API が追加されました。これらの変更の詳細については、プロジェクトの変更履歴を参照してください。
Version 22.0.0
https://github.com/oracle/graal/blob/master/truffle/CHANGELOG.md#version-2200
Conclusion
このリリースに寄せられたすべてのフィードバック、提案、貢献に対して、コミュニティのみなさまに感謝します。このリリースに関する追加のフィードバックや、今後の機能に関する提案がありましたら、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