原文はこちら。
The original article was written by Olga Gupalo (Member of Tech Staff for GraalVM at Oracle).
https://medium.com/graalvm/native-image-quick-reference-graalvm-for-jdk-23-45cb94b65bf7
Native Image Quick Referenceの改訂版を出してから2年が過ぎ、その頃から大きな変更が加わっています。
Native Image Quick Reference v2
https://medium.com/graalvm/native-image-quick-reference-v2-332cf453d1bc
https://logico-jp.dev/2022/11/18/native-image-quick-reference-v2/
最大のアップデートは、Native ImageがGraalVMディストリビューションに含まれ、別途ダウンロードしなくてもよくなった点です。しばらくの間、開発者体験の改善に注力してきました。その中には、ビルド時間の短縮、有用な新機能の追加、パブリックAPIになった幅広く使われるホストオプションの包含、といったものがあります。このQuick Referenceでは、特に最適化およびパフォーマンスに関するNative Imageの主要なアップデートを取り上げます。

このQuick ReferenceはA4もしくはUSレターサイズに合うようデザインされており、印刷しやすくなっています。PDF版は以下からダウンロードできます。
- A4 Paper — Download PDF
- US Letter Paper — Download PDF
Building
JavaアプリケーションをAOTビルドするには2種類のやり方があります。一つはnative-imageユーティリティをコマンドラインから利用する方法、もう一つはNative Build Tooksを使う方法です。
native-imageユーティリティをコマンドラインで実行してビルドする方法は、javaコマンドを使う場合と同じで簡単です。
| クラスのコンパイル | native-image [options] <mainclass> [args…] |
| JARファイルのコンパイル | native-image [options] -jar <jarfile> [args…] |
| モジュールのmainクラスのコンパイル | native-image [options] -m <module>[/<mainclass>] [args…] |
共有ライブラリのビルドには変更がなく、native-imageツールに--sharedオプションを渡す必要があります。
コンテナデプロイ向けには、Native Imageのコンテナ化・リンクオプション(Linuxのみ)を活用し、ランタイムライブラリの依存関係を削減することを推奨します。--static --libc=muslオプションで完全に静的なnative image(fully static native image)を作成すると、軽量コンテナで(scratchコンテナでもOK)実行でき、ランタイムの依存関係は必要ありません。
もう一つのオプションは、 --static-nolibc を使ってほとんど静的リンクされたバイナリ(mostly statically linked binary)をビルドする、というものです。このほとんど静的リンクされたnative imageとは、libc (標準Cライブラリ) 以外の、依存関係を持つ全ての共有ライブラリ (zlib、JDKの静的共有ライブラリ) をリンクしています。このタイプのnative imageはglibcが提供されているDistrolessベースコンテナにデプロイする場合に有用です。
Native Build Tools
native imageのビルドの推奨方法は、MavenやGradle向けのNative Image Buildプラグインを使う方法です。
Native Build Tools
https://graalvm.github.io/native-build-tools/latest/
Mavenプロジェクトで、Mavenプロファイルとしてプラグインをpom.xmlに定義します。
<profile>
<id>native</id>
<build>
<plugins>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<version>${buildtools.version}</version>
<executions>
<execution>
<id>build-native</id>
<goals>
<goal>compile-no-fork</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
シンプルに以下のように呼び出すとビルドできます。
./mvnw -Pnative package
必要に応じて、 -DskipTests を付けてテストをスキップしてください。
./mvnw -DskipTests -Pnative package
Gradleプロジェクトでは、 build.gradle でプラグインを定義します(Kotlin用の構成はプラグインのドキュメントに記載があります)。
Gradle plugin for GraalVM Native Image building
https://graalvm.github.io/native-build-tools/latest/gradle-plugin.html
plugins {
id 'org.graalvm.buildtools.native' version '${buildtools.version}'
}
以下のコマンドで、ネイティブ実行ファイルのビルドとアプリケーションの実行をまとめて実施できます。
./gradlew nativeRun
両方のプラグインとも、ネイティブコードとしてJUnit Platformテストをサポートします。
| Maven | ./mvnw -Pnative test |
| Gradle | ./gradlew nativeTest |
Native Image Maven/Gradleプラグインの基本的な構成を大幅に拡張できます。例えばMavenプロジェクトだと、直接プラグイン構成を使ってオプションをnative-imageユーティリティに渡すことができます。
<configuration>
<buildArgs>
<buildArg>--option</buildArg>
</buildArgs>
</configuration>
Gradleプロジェクトの場合は以下のようです。
graalvmNative {
binaries {
main {
buildArgs.add("--option")
}
}
}
詳細はプラグインのドキュメントをご覧ください。
Native Build Tools
https://graalvm.github.io/native-build-tools/latest/
Optimizing
native-imageユーティリティは、パフォーマンス、サイズ、ビルド時間、デバッグのしやすさ、その他のメトリックに合わせて、生成されたネイティブ実行ファイルを最適化する複数の方法を提供します。
Improved performance
パフォーマンス向上、特にスループットの向上のために、Profileガイド付き最適化(PGO: Profile-Guided Optimization)を使うことを強く推奨します。PGOを有効化すると、Graalコンパイラがプロファイル情報を活用できるようになり、最適化レベル-O3に設定されます。これはアプリケーションをAOTコンパイルする際にGraalコンパイラがJITコンパイラとして実行する場合と同様です。このためには以下の手順を実行します。
--pgo-instrumentを付けてアプリケーションをビルドする- 代表的なワークロードでインストルメント済みのアプリケーションを実行し、プロファイルデータを作成し、
default.iprofとしてデータを保存する。 --pgoオプションを使って再ビルドする(もしくは、 特定のプロファイルを指定するならば、--pgo=<custom>.iprofで指定できる)と、最適化されたネイティブアプリケーションが生成される。
このトピックに関する詳細情報は、以下のドキュメントに記載があります(Oracle GraalVMでのみ利用可能です)。
Basic Usage of Profile-Guided Optimization
https://www.graalvm.org/latest/reference-manual/native-image/optimizations-and-performance/PGO/basic-usage/
次の推奨事項は、デフォルトのシリアルGCからG1 GCへ切り替えることで、レイテンシやスループットを改善する、というものです。native-imageユーティリティに --gc=G1 オプションを渡すことで、G1を有効化できます(Oracle GraalVMかつLinuxの場合のみ利用可能)。以下はパフォーマンスチューニング時に指定できるオプションの例です。
-H:G1HeapRegionSize | G1のリージョンサイズを定義 |
-XX:MaxRAMPercentage | 最大ヒープサイズを物理メモリのパーセンテージとして設定 |
-XX:MaxGCPauseMillis | 最大目標ポーズ時間を設定 |
完全なリストは以下のドキュメントをご覧ください。
Performance Tuning
https://www.graalvm.org/latest/reference-manual/native-image/optimizations-and-performance/MemoryManagement/#performance-tuning
例えば、リージョンサイズを2MB、最大目標ポーズ時間を100msecに設定したG1GCを使うnative imageを作成する場合、以下のようなコマンドを実行します。
native-image --gc=G1 -H:G1HeapRegionSize=2m -R:MaxGCPauseMillis=100 HelloWorld
上記のコマンドでビルドしたnative imageを実行する際に、最大目標ポーズ時間をオーバーライドするには、以下のようにします。
./helloworld -XX:MaxGCPauseMillis=50
PGOを使わずに最適なパフォーマンスを得るために、新たな-O3オプションを利用できますが、その代わりにビルド時間が長くなる可能性があります。
別のオプションは、特定のマシンタイプに対しての最適化です。同じマシンまたは同様のスペックのマシンにアプリケーションをデプロイする場合、-march=nativeオプションを使用して、より多くのCPU機能を有効にできます。これにより、バイナリが生成されたマシンで利用可能なすべての命令を使用するようにコンパイラに指示します。
生成されたイメージが、さまざまな、そして場合によっては非常に古いマシンを使用するユーザーに配布される場合は、-march=compatibility を使用します。これにより、コンパイラが使用する命令セットが最小限に抑えられ、生成されたバイナリの互換性が向上します。
For faster build time
ビルド時間を短縮するためには、 -Obでクイックビルドモードを有効にします。このモードはコンパイラ最適化を減らし、トータルのコンパイルフェーズ時間を削減するという、開発ビルド向けのモードです。しかしながら、productionビルドは完全な最適化のメリットを享受します。多くのCPUコアがあれば、ビルド時間は劇的に改善します。CPUが多ければ、より高速なビルドが可能です。
Compiling Methods
https://www.graalvm.org/latest/reference-manual/native-image/overview/BuildOutput/#stage-compiling
また、native-imageはGC、ピークRSS、CPU負荷などのリソース使用統計情報と共に、ビルド出力をコンソールに書き出します。
Resource Usage Statistics
https://www.graalvm.org/latest/reference-manual/native-image/overview/BuildOutput/#resource-usage-statistics
For smaller binary size
-Os オプションを使うと、より小さなサイズの実行ファイル生成に重きを起きます。これは占有スペースが問題となる、クラウドやコンテナといったデプロイ環境に最適です。このオプションは -O2 から重要な最適化を維持しつつも、パフォーマンスが低下するというデメリットを伴います。
For reduced memory footprint
ネイティブイメージを実行する際、Javaヒープ設定はシステム構成とGCに基づいて決まります。このデフォルトのメカニズムを上書きするには、実行時の予測可能なメモリ使用量を実現するために最大ヒープサイズを設定します。
./myapp -Xms2m -Xmx10m -Xmn1m
期待通りの結果が得られなかった場合には、GCログを収集しておくとよいでしょう。
./myapp -XX:+PrintGC -XX:+VerboseGC
詳細情報は、以下のドキュメントに記載があります。
Java Heap Size
https://www.graalvm.org/latest/reference-manual/native-image/optimizations-and-performance/MemoryManagement/#java-heap-size
Useful Developer Tools
ネイティブバイナリのファイルサイズが想定外に増大した場合、--emit build-report オプションを付けてBuild Reportを生成し、埋め込まれたリソースとその他のデータを分析できるようになりました。Build Reportsを使うとコマンドラインよりも詳細かつ魅力的な方法で、生成されたイメージの内容を詳しく調べることができます。以下のドキュメントに例があります(Oracle GraalVMでのみ利用可能)。
Optimize Size of a Native Executable using Build Reports
https://www.graalvm.org/jdk23/reference-manual/native-image/guides/optimize-native-executable-size-using-build-report/
アプリケーションの依存関係を識別するのであれば、デフォルトでCycloneDXをサポートする新たな--enable-sbomオプションを付けて、SBOM (Software Bill of Materials、ソフトウェア部品表) を生成できます。これにより、GZIP圧縮されたSBOMファイルが埋め込まれますが、後から人間が読める形式に展開したり、脆弱性スキャナー (vulnerability scanner) に投げ込むことができます(Oracle GraalVMでのみ利用可能)。
Embed an SBOM in a Native Executable to Identify Its Dependencies
https://www.graalvm.org/latest/reference-manual/native-image/guides/use-sbom-support/
Native Image は、Java Management Extensions (JMX)、Native Memory Tracking (NMT) など、以前よりも多くのアプリケーション監視機能をサポートするようになりました。 --enable-monitoring オプションを使用してビルド時に監視機能を有効にできます(デフォルトではall)。 選択した機能のみを有効にするには、heapdump、jfr、jvmstat、jmxserver、jmxclient、nmt、threaddumpのいずれかを選択します。 これらの機能のいずれかを有効にすると、実行可能ファイルのサイズが大きくなります。
パフォーマンス分析用に、Native Image はLinux Perf プロファイラをサポートするようになりました。
perf: Linux profiling with performance counters
https://perfwiki.github.io/main/#perf-linux-profiling-with-performance-counters
perf を使用して、以下のように実行時の統計情報を収集できます。
perf stat ./myapp
プロファイリングの詳細については、以下のドキュメントをご覧ください。
Linux Perf Profiler Support in Native Image
https://www.graalvm.org/latest/reference-manual/native-image/debugging-and-diagnostics/perf-profiler/
Summary
アップデートされたQuick Referenceは、開発者が最新のNative Image機能に追随できるようにデザインされています。 ビルド、最適化、開発者ツールのオプションなど、重要な内容が網羅されています。 コンテナ向けにビルドしている場合、最適化されたパフォーマンスを目指している場合、または複雑な問題をデバッグしている場合など、どのような場合でも、このQuick Referenceが最適なスタート地点となるでしょう。
ダウンロードして印刷し、手元に置いておいてください。GraalVM Native Imageで楽しくコーディングしましょう!