GraalVM で Java ネイティブイメージビルドの Hello World!
目的
Java のプログラムをネイティブイメージビルドします。
実現すること
ローカル環境 (Ubuntu) で Java アプリを GraalVM を使用してネイティブイメージビルドし、それを実行してみます。
技術背景
Java におけるネイティブイメージビルドとは?
こちらを展開してご覧いただけます。
ネイティブイメージビルド
Java コードをネイティブマシンコードにコンパイルすることです。通常、Java コードは Java バイトコードと呼ばれる中間言語にコンパイルされ、Java 仮想マシン(JVM)で実行されます。しかし、ネイティブイメージビルドは、Java コードを JVM を介さずに直接実行可能なネイティブマシンコードに変換することで、より高速な実行速度とより低いメモリ使用量を実現することができます。
ネイティブイメージビルドは、以下のようなニーズから求められています。
パフォーマンスの向上
Java は一般的に高水準のプログラミング言語であり、JVM によって実行されるため、実行速度が遅いとされることがあります。ネイティブイメージビルドにより、高速な実行速度を実現することができます。
メモリの最適化
JVM による Java コードの実行には、多くのメモリが必要となることがあります。ネイティブイメージビルドにより、より少ないメモリ使用量でプログラムを実行できるようになります。
ネイティブプログラムの統合
Java は、C言語や C++ などの他のプログラミング言語で書かれたネイティブプログラムと統合することができます。しかし、統合するためには、ネイティブコードが必要になります。ネイティブイメージビルドにより、これらのネイティブプログラムと Java コードをシームレスに統合することができます。
開発環境
- Windows 11 Home 22H2 を使用しています。
- WSL の Ubuntu を操作していきますので macOS の方も参考にして頂けます。
WSL (Microsoft Store アプリ版)
> wsl --version
WSL バージョン: 1.0.3.0
カーネル バージョン: 5.15.79.1
WSLg バージョン: 1.0.47
Ubuntu
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 22.04.1 LTS
Release: 22.04
JDK (GraalVM)
$ java -version
openjdk version "17.0.6" 2023-01-17
OpenJDK Runtime Environment GraalVM CE 22.3.1 (build 17.0.6+10-jvmci-22.3-b13)
OpenJDK 64-Bit Server VM GraalVM CE 22.3.1 (build 17.0.6+10-jvmci-22.3-b13, mixed mode, sharing)
※ この記事では基本的に Ubuntu のターミナルで操作を行います。
"Hello World" を表示する手順
GraalVM ビルド環境
※ 以前の記事を参考にして頂けます。
GraalVM ビルド環境
プロジェクトフォルダの作成
※ ~/tmp/hello-graalvm をプロジェクトフォルダとします。
$ cd ~
$ mkdir -p tmp/hello-graalvm
$ cd ~/tmp/hello-graalvm
Java クラスの作成
$ vim HelloWorld.java
ファイルの内容
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
JDK のバージョン確認
※ GraalVM が選択されているか確認してください。
$ java -version
openjdk version "17.0.6" 2023-01-17
OpenJDK Runtime Environment GraalVM CE 22.3.1 (build 17.0.6+10-jvmci-22.3-b13)
OpenJDK 64-Bit Server VM GraalVM CE 22.3.1 (build 17.0.6+10-jvmci-22.3-b13, mixed mode, sharing)
コンパイル
$ javac HelloWorld.java
$ native-image HelloWorld
詳細な出力を表示します。
$ native-image HelloWorld
========================================================================================================================GraalVM Native Image: Generating 'helloworld' (executable)...
========================================================================================================================[1/7] Initializing... (5.0s @ 0.14GB) Version info: 'GraalVM 22.3.1 Java 17 CE'
Java version info: '17.0.6+10-jvmci-22.3-b13'
C compiler: gcc (linux, x86_64, 11.3.0)
Garbage collector: Serial GC
[2/7] Performing analysis... [*****] (16.0s @ 0.82GB)
2,818 (73.16%) of 3,852 classes reachable
3,385 (50.80%) of 6,664 fields reachable
12,420 (42.77%) of 29,038 methods reachable
27 classes, 0 fields, and 313 methods registered for reflection
58 classes, 58 fields, and 52 methods registered for JNI access
4 native libraries: dl, pthread, rt, z
[3/7] Building universe... (1.8s @ 1.31GB)
[4/7] Parsing methods... [*] (1.4s @ 0.47GB)
[5/7] Inlining methods... [***] (0.9s @ 0.85GB)
[6/7] Compiling methods... [****] (17.2s @ 1.80GB)
[7/7] Creating image... (2.0s @ 2.22GB)
3.99MB (34.91%) for code area: 7,101 compilation units
6.91MB (60.43%) for image heap: 97,085 objects and 5 resources
546.30KB ( 4.66%) for other data
11.44MB in total
------------------------------------------------------------------------------------------------------------------------
Top 10 packages in code area: Top 10 object types in image heap:
659.64KB java.util 909.84KB java.lang.String
328.29KB java.lang 881.94KB byte[] for code metadata
264.68KB java.text 867.93KB byte[] for general heap data
216.40KB java.util.regex 618.42KB java.lang.Class
194.78KB java.util.concurrent 542.30KB byte[] for java.lang.String
146.95KB java.math 441.14KB java.util.HashMap$Node
117.08KB java.lang.invoke 220.16KB com.oracle.svm.core.hub.DynamicHubCompanion
114.90KB com.oracle.svm.core.genscavenge 212.92KB java.util.HashMap$Node[]
103.72KB java.util.logging 160.63KB java.lang.String[]
95.20KB java.util.stream 154.92KB java.util.concurrent.ConcurrentHashMap$Node
1.75MB for 118 more packages 1.52MB for 769 more object types
------------------------------------------------------------------------------------------------------------------------
0.7s (1.4% of total time) in 17 GCs | Peak RSS: 3.19GB | CPU load: 6.12
------------------------------------------------------------------------------------------------------------------------
Produced artifacts:
/home/$USER/tmp/hello-graalvm/helloworld (executable)
/home/$USER/tmp/hello-graalvm/helloworld.build_artifacts.txt (txt)
========================================================================================================================
Finished generating 'helloworld' in 46.1s.
ディレクトリ構成
$ tree
.
├── HelloWorld.class
├── HelloWorld.java
├── helloworld
└── helloworld.build_artifacts.txt
$ ls -lah
合計 12M
drwxr-xr-x 2 $USER $USER 4.0K 2月 24 12:40 .
drwxr-xr-x 29 $USER $USER 4.0K 2月 24 12:38 ..
-rw-r--r-- 1 $USER $USER 426 2月 24 12:39 HelloWorld.class
-rw-r--r-- 1 $USER $USER 123 2月 24 12:38 HelloWorld.java
-rwxr-xr-x 1 $USER $USER 12M 2月 24 12:40 helloworld
-rw-r--r-- 1 $USER $USER 25 2月 24 12:40 helloworld.build_artifacts.txt
※ helloworld というネイティブイメージビルドのアプリケーションが作成されています。
実行
$ ./helloworld
Hello World!
ターミナルに "Hello World!" と表示することが出来ました。
まとめ
- Ubuntu 環境にて GraalVM を使用して Java のネイティブイメージビルドアプリを作成、実行することが出来ました。