Native Image Quick Reference v2

原文はこちら。
The original article was written by Olya Gupalo (Member of Tech Staff for GraalVM at Oracle) and Fabio Niephaus (Researcher on the GraalVM team at Oracle Labs).
https://medium.com/graalvm/native-image-quick-reference-v2-332cf453d1bc

ネイティブ・イメージは、GraalVMの最も人気のある機能の1つで、最初のNative Image Quick Referenceを発行してから、かなりの時間が経ちました。

GraalVM Native Image Quick Reference v1
https://medium.com/graalvm/graalvm-native-image-quick-reference-4ceb84560fd8
https://logico-jp.io/2021/02/06/graalvm-native-image-quick-reference-v1/

それ以来、多くの機能要求が実装され、新しいコマンドラインオプションが導入されたり非推奨になったりして、ツールのサポートが拡張されました。最も重要なオプション、コマンド、およびリンクをこの 1 ページにまとめました。

このクイックリファレンスは、A4用紙1枚に収まるサイズなので、PDF版を印刷して、便利な備忘録として利用できます。お使いのプリンタが US Letter用紙形式を使用する場合は、US Letter版をダウンロードしてください。

Native Imageを使い始める前に、GraalVMのJDKをダウンロードしてください。

Download GraalVM
https://www.graalvm.org/downloads/

最も簡単な方法は、GraalVM JDK Downloaderを実行することです。

$ bash <(curl -sL https://get.graalvm.org/jdk)

GraalVM JDK Downloader
https://github.com/graalvm/graalvm-jdk-downloader

Build Native Executables and Shared Libraries

Native Imageは、Javaアプリケーションをネイティブの実行ファイルと共有ライブラリに変換できます。これらのバイナリファイルには、ユーザーコード、依存関係、およびJavaランタイムの必要な部分(例えばガベージコレクタなど)が含まれています。入力ソースは、アプリケーションのすべての依存関係を持つJARファイル、モジュール、またはアプリケーションのメインクラスのいずれかです。GraalVMネイティブ・イメージは、現在ではJava Platform Module Systemをサポートしています。

JSR 376: JavaTM Platform Module System
https://www.jcp.org/en/jsr/detail?id=376

$ native-image --jar Foo.jar                # for a JAR
$ native-image -p Foo.jar -m org.foo.Main   # for a module
$ native-image -cp Foo.jar org.foo.Main     # for a class

ここで、$JAVA_HOME/bin/native-image$JAVA_HOME/bin/javaと同じように使えることを強調しておきます。javaコマンドとデフォルトの動作をあわせています。base-module.jarmain-module.jarの2つのJavaモジュール(demo.appはメインクラスを含む)から実行ファイルを構築する方法を見てみましょう。

$ native-image -p base-module.jar:main-module.jar -m demo.app

Native Imageがアプリケーションとその依存関係を静的に分析し、最適化し、そして到達可能なすべてのコードをコンパイルする必要があるため、ネイティブ実行ファイルのビルドにはある程度の時間がかかることがあります。このプロセスを高速化するために、開発用にクイックビルドモード、-Ob(大文字の “O”、小文字の “b”)を有効にできます。これにより、コンパイラーはエコノミーモードで動作し、最適化の度合いを抑え、コンパイル時間を大幅に短縮します。したがって、ランタイムパフォーマンスとメモリ使用量を犠牲にしてビルド時間を最適化するので、実運用にはお勧めできません。

$ native-image -Ob MyApp

新しいオプション -o を使用すると、生成されるバイナリに名前を設定することもできます。例を示します。

$ native-image -jar Foo.jar -o myapp

クラスパスまたはモジュールパスでクラスが見つからない場合でも、ビルドに失敗しなくなりました。しかし、そのようなクラスが実行時に明示的に必要とされる場合、ネイティブ実行ファイルは(例えば、java.lang.NotClassDefFoundErrorで)失敗します。このようなシナリオを防ぐために、Native Imageでは新しい --link-at-build-time オプションを提供しています。これにより、ビルド時にパッケージとクラスの特定のリストが利用可能でリンク済みであることを確認します。

AOTアプローチを最大限に活用するために、Native Imageではビルド時にクラスの初期化が可能で、実行時に必要な作業を減らしてパフォーマンスを向上できます。クラスの初期化は --initialize-at-run-time=my.package,some.class--initialize-at-build-time=my.package, some.class オプションを使って手動で設定できます。初期化されたクラスの静的フィールドは、生成された実行ファイルに格納されます。詳しくはドキュメントをご覧ください。

Class Initialization in Native Image
https://www.graalvm.org/22.3/reference-manual/native-image/optimizations-and-performance/ClassInitialization/

すでに述べたように、ネイティブ実行形式と共有ライブラリをビルドできます。後者をビルドするには、引数--sharedを渡します。その後、例えばC/C++アプリケーションからNative Image C APIを使って、このライブラリを呼び出したりロードしたりできます。

Build a Native Shared Library
https://www.graalvm.org/22.3/reference-manual/native-image/guides/build-native-shared-library/

これら2種類のバイナリの他に、libcの実装に対して静的にリンクできます。デプロイメント環境に応じて、glibmuslbionicに対してリンクできます。

--static --libc=<glib | musl | bionic>

リンクされたバイナリは、slimコンテナやdistrolessコンテナ(スクラッチコンテナ)に簡単に配布・デプロイできます。以下のURLに例があります。

Build a Statically Linked or Mostly-Statically Linked Native Executable
https://www.graalvm.org/22.3/reference-manual/native-image/guides/build-static-executables/

パフォーマンスを最適化するためのもう一つのアクションは、別のGC実装に切り替えることです。Native Imageは、現在、新しいSerial GCのポリシーを用意しており、実行時にアプリケーションのメモリフットプリントを削減しますが、新しいEpsilon GCのポリシーも用意しています。デフォルトのGCはSerial GCで、Epsilon GCは、短時間実行のアプリケーションのための何もしないガベージコレクタ (no-op garbage collector) です。Epsilon GCを有効にするには、オプションで--gc=epsilonを指定します。GraalVM Enterprise Native Imageは、G1 GCもサポートしており、--gc=G1オプションで有効にできます。

さらに、GraalVM EnterpriseのNative Imageでは、脆弱性スキャナーを支援するために、 ネイティブ実行ファイルにソフトウェア部品表 (SBOM) の埋め込みが可能です。これには、--enable-sbom というビルド時オプションを使用します(現在、CycloneDX 形式のみサポート)。このオプションを有効にすると、Inspectionツールを使って、以下のコマンドで圧縮されたSBOMを抽出することができます。

$ native-image-inspect --sbom <path_to_binary>

Native Image Inspection Tool
https://www.graalvm.org/22.3/reference-manual/native-image/debugging-and-diagnostics/InspectTool/

もう一つの重要なオプションは --enable-url-protocols です。これはカンマで区切られたURLプロトコルのリストを受け取ります。fileresourceのプロトコルはデフォルトで有効になっており、httphttpsは必要に応じて追加できます。このようにして、バイナリは必要な機能のみを含むようになり、全体のサイズを小さく保つことができます。オプションの--install-exit-handlersは、シグナルハンドラを含めるためのもので、Dockerコンテナでネイティブ実行ファイルをビルドする際に望ましいオプションです。最後にお伝えしたいのは、特定のGraalVM言語ランタイムを有効にすることで、polyglotネイティブ実行ファイルをビルドできる点です。

--language:<java | js | python | ruby | wasm>

GCポリシーをチューニングしてパフォーマンスを最適化する以外に、ピークパフォーマンスとスループットを向上させるために、プロファイルガイド付き最適化(GraalVM Enterpriseで利用可能)を有効にする方法もあります。

Optimize a Native Executable with Profile-Guided Optimizations
https://www.graalvm.org/22.3/reference-manual/native-image/guides/optimize-native-executable-with-pgo/

まず、プロファイリングデータ取得用ネイティブ実行ファイルをビルドします。

native-image --pgo-instrument MyApp

次に、これを実行してプロファイルを記録します。

./myapp

最後に、プロファイリングデータに基づいて最適化されたネイティブ実行ファイルをビルドします。

Native Image のオプションの完全なリストは、--help および --help-extra オプションを使って確認できます。

Native Image Integrations

昨年、Native Imageのために新しい統合が数多くなされました。JUnit、Micronaut、Springの各チームと協力して、Native Build Toolsを開発し、ネイティブのJUnit 5テストを標準でサポートするNative Image用のGradleとMavenプラグインを提供しました。これらのプラグインで、Javaアプリケーションをネイティブ実行ファイルとしてビルド、テスト、実行が簡単になります。

Native-image plugins for various build tools
https://github.com/graalvm/native-build-tools
Gradle plugin for GraalVM Native Image building
https://graalvm.github.io/native-build-tools/latest/gradle-plugin
Maven plugin for GraalVM Native Image building
https://graalvm.github.io/native-build-tools/latest/maven-plugin
Gradle and Maven plugins for Native Image with initial JUnit testing support
https://medium.com/graalvm/gradle-and-maven-plugins-for-native-image-with-initial-junit-testing-support-dde00a8caf0b
https://logico-jp.io/2021/06/17/gradle-and-maven-plugins-for-native-image-with-initial-junit-5-testing-support/

ワークフローは、Java開発者であれば誰でも知っているものです。プラグインを pom.xml や build.gradle に登録し、プラグインのリポジトリを有効にしてすると、ほらできあがり。Mavenであれば

./mvnw -Pnative package

Gradleであれば

./gradlew nativeRun

で、ネイティブ実行ファイルを直接ビルドできるようになります。プラグインの設定で、任意のNative Imageオプションを渡すことができることに注意してください。以下に例を示します。

<buildArgs>
  <arg>--enable-url-protocols=https</arg>
</buildArgs>

包括的なMaven / Gradleプラグインのドキュメントをチェックするか、以下のWebサイトのデモをチェックしてください。

Gradle plugin for GraalVM Native Image building
https://graalvm.github.io/native-build-tools/latest/gradle-plugin
Maven plugin for GraalVM Native Image building
https://graalvm.github.io/native-build-tools/latest/maven-plugin
Use Gradle to Build a Native Executable from a Java Application
https://www.graalvm.org/22.3/reference-manual/native-image/guides/use-native-image-gradle-plugin/
Use Maven to Build a Native Executable from a Java Application
https://www.graalvm.org/22.3/reference-manual/native-image/guides/use-native-image-maven-plugin/

もう一つの新しい統合は、GitHub Action for GraalVMです。これにより、GitHub Actions上で動作するあなたのプロジェクトの自動デプロイメントワークフローの中で、ネイティブ実行ファイルのビルドが可能になります。

GitHub Action for GraalVM
https://github.com/marketplace/actions/github-action-for-graalvm

Enhanced Third-Party Library Support

ネイティブ実行ファイルをビルドすると、そのファイルには静的解析によって検出された要素のみが含まれます。必要なクラス、メソッド、またはフィールドへビルド時に到達できずに、バイナリに含まれていない場合、アプリの実行時に問題が発生する可能性があります。静的解析でJavaの動的機能呼び出し(リフレクション、プロキシなど)をすべて検出できるようにするには、追加の構成を提供する必要がありますが、それには3つの方法があります。

1. Micronaut、Helidon、Spring、QuarkusのようなNative Imageサポートを内蔵したフレームワークを使用する。

2. Maven/Gradleプラグインを使用して、GraalVM Reachability Metadata Repositoryから自動的に設定を取り込む。

Repository which contains community-driven collection of GraalVM reachability metadata for open-source libraries
https://github.com/oracle/graalvm-reachability-metadata

3. 手作業で設定する。Tracing Agentはこのプロセスを支援できる。Tracing Agentを使ってJavaアプリケーションを実行し、構成を生成し、この構成を引き込んでネイティブ実行可能ファイルをビルドする。

Collect Metadata with the Tracing Agent
https://graalvm.org/22.3/reference-manual/native-image/metadata/AutomaticMetadataCollection/

$ java -agentlib:native-image-agent=config-output-dir=/path/to/config-dir/ -jar MyApp.jar
$ native-image -jar MyApp.jar

クラスパスやモジュールパス上のMETA-INF/native-imageにある構成ファイルを自動的に含める。

Monitoring and Debugging

最近GraalVMチームでは実行時のアプリケーション監視に注目しています。チームでは、ビルド時に問題を発見するだけでなく、実行時にパフォーマンスを最適化するのに役立つ優れたツールをユーザーに提供したいと考えています。

--enable-monitoringオプションを付けてネイティブ実行ファイルのビルドを実施すると、多くのモニタリングの可能性が開かれ、実行時にVMを検査できます。カンマ区切り引数のリストに、heapdumpjfrjvmstat、またはall(デフォルトはall)を含めることができます。例えば、JFRを有効にして、実行時にJFRイベントの記録を開始できます。

JDK Flight Recorder (JFR) with Native Image
https://graalvm.org/22.3/reference-manual/native-image/debugging-and-diagnostics/JFR/

$ ./myapp -XX:+FlightRecorder \
     -XX:StartFlightRecording=“filename=recording.jfr”

また、実行時にネイティブ実行ファイルのヒープダンプも取得できます。ヒープダンプ取得には様々な方法があります。

  • SIGUSR1シグナルを送信する
  • プログラムで行う
  • コマンドラインオプション-XX:+DumpHeapAndExitを使用する、など


ヒープダンプはネイティブ実行ファイルのディレクトリに作成され、VisualVMなどのJavaヒープダンプツールで解析できます。

VisualVM
https://visualvm.github.io

詳しくは以下をご覧ください。

Create a Heap Dump from a Native Executable
https://graalvm.org/22.3/reference-manual/native-image/guides/create-heap-dump/

さらに、ガベージコレクタをチューニングしたり、GCログを収集したりすることもできます。

$ ./myapp -Xmx<m> -Xmn<m> …
$ ./myapp -XX:+PrintGC -XX:+VerboseGC

大事なことを言い忘れていましたが、デバッグのサポートも大幅に改善されました。デバッグ情報付きかつコンパイラの最適化を無効にするオプション(-g -O0)を付けてネイティブ実行ファイルをビルドし、GDBまたはGDBを統合したIDEでデバッグしてください。例えば、IntelliJ IDEAは、GDBに基づくNative Imageのデバッグを実験的にサポートしています。ぜひお試しの上、感想をお寄せください。

IntelliJ IDEA 2022.2 EAP 5: Support for Spring 6 and Spring Boot 3 Features, Enhanced HTTP Client, Kubernetes Updates and More
https://blog.jetbrains.com/idea/2022/06/intellij-idea-2022-2-eap-5/#Experimental_GraalVM_Native_Debugger_for_Java

Conclusion

過去数回のリリースでは、ビルド時間とメモリフットプリントを削減し、全体的な開発者エクスペリエンスを向上させることに開発の重点を置きました。サードパーティライブラリに依存するアプリケーションのネイティブ実行ファイルのビルドは、Native Image Build ToolsとGraalVM Reachability Metadata Repositoryのおかげで、非常に簡単になりました。

Native Build Tools
https://graalvm.github.io/native-build-tools/latest/index.html
Repository which contains community-driven collection of GraalVM reachability metadata for open-source libraries.
https://github.com/oracle/graalvm-reachability-metadata

このQuick Referenceでは、この1年間で最も頻繁に使用されたオプションとGraalVM Native Imageの新しい統合について概説しています。

このQuick Referenceがお役に立つことを願っています。フィードバックがありましたら、Slack、Twitter、もしくはGitHubのIssueでお寄せください。

Slack invitation
https://www.graalvm.org/slack-invitation
GitHub Issues
https://github.com/oracle/graal
Twitter
https://twitter.com/graalvm

コメントを残す

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

WordPress.com ロゴ

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

Twitter 画像

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

Facebook の写真

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

%s と連携中