GraalVM で Java ネイティブイメージビルドの Hello World!

# GraalVM で Java ネイティブイメージビルドの Hello World!

## 目的

Java のプログラムをネイティブイメージビルドします。  

## 実現すること

ローカル環境 (Ubuntu) で Java アプリを GraalVM を使用してネイティブイメージビルドし、それを実行してみます。  

## 技術背景

### Java におけるネイティブイメージビルドとは？  

<details>
<summary>こちらを展開してご覧いただけます。</summary>

### ネイティブイメージビルド

Java コードをネイティブマシンコードにコンパイルすることです。通常、Java コードは Java バイトコードと呼ばれる中間言語にコンパイルされ、Java 仮想マシン（JVM）で実行されます。しかし、ネイティブイメージビルドは、Java コードを JVM を介さずに直接実行可能なネイティブマシンコードに変換することで、より高速な実行速度とより低いメモリ使用量を実現することができます。  

ネイティブイメージビルドは、以下のようなニーズから求められています。  

### パフォーマンスの向上

Java は一般的に高水準のプログラミング言語であり、JVM によって実行されるため、実行速度が遅いとされることがあります。ネイティブイメージビルドにより、高速な実行速度を実現することができます。

### メモリの最適化

JVM による Java コードの実行には、多くのメモリが必要となることがあります。ネイティブイメージビルドにより、より少ないメモリ使用量でプログラムを実行できるようになります。

### ネイティブプログラムの統合

Java は、C言語や C++ などの他のプログラミング言語で書かれたネイティブプログラムと統合することができます。しかし、統合するためには、ネイティブコードが必要になります。ネイティブイメージビルドにより、これらのネイティブプログラムと Java コードをシームレスに統合することができます。

</details>

## 開発環境

+ Windows 11 Home 22H2 を使用しています。  
+ [WSL](https://learn.microsoft.com/ja-jp/windows/wsl/about) の Ubuntu を操作していきますので macOS の方も参考にして頂けます。  

<details>

WSL (Microsoft Store アプリ版)

```sh
> wsl --version
WSL バージョン: 1.0.3.0
カーネル バージョン: 5.15.79.1
WSLg バージョン: 1.0.47
```

Ubuntu

```sh
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 22.04.1 LTS
Release:        22.04
```

JDK (GraalVM)

```sh
$ 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)
```

</details>

※ この記事では基本的に Ubuntu のターミナルで操作を行います。

## "Hello World" を表示する手順

### GraalVM ビルド環境

※ 以前の記事を参考にして頂けます。  
[GraalVM ビルド環境](https://qiita.com/hiroxpepe/items/8650664e466906a57996#graalvm-%E3%83%93%E3%83%AB%E3%83%89%E7%92%B0%E5%A2%83)  

### プロジェクトフォルダの作成

※ ~/tmp/hello-graalvm をプロジェクトフォルダとします。

```sh
$ cd ~
$ mkdir -p tmp/hello-graalvm
$ cd ~/tmp/hello-graalvm
```

### Java クラスの作成

```sh
$ vim HelloWorld.java
```

ファイルの内容

```java:HelloWorld.java
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello World!");
    }
}
```

### JDK のバージョン確認  

※ GraalVM が選択されているか確認してください。  

```sh
$ 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)
```

### コンパイル

```sh
$ javac HelloWorld.java
$ native-image HelloWorld
```

<details>
<summary>詳細な出力を表示します。</summary>

```sh
$ 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.
```

</details>


### ディレクトリ構成

```sh
$ tree
.
├── HelloWorld.class
├── HelloWorld.java
├── helloworld
└── helloworld.build_artifacts.txt
```

```sh
$ 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** というネイティブイメージビルドのアプリケーションが作成されています。  

### 実行

```sh
$ ./helloworld
Hello World!
```

ターミナルに "Hello World!" と表示することが出来ました。

## まとめ

+ Ubuntu 環境にて GraalVM を使用して Java のネイティブイメージビルドアプリを作成、実行することが出来ました。  

## 参考

+ [GraalVM Java Reference](https://www.graalvm.org/latest/reference-manual/java/)