Build a Native Image of a Java and Kotlin Application

This example demonstrates how to compile a Java and Kotlin application ahead-of-time into a native executable, and illustrates the advantages.

Preparation #

1. Download or clone the repository and navigate into the java-kotlin-aot directory:

Note: You can use any JDK for building the application. However, javac from GraalVM in the build script is used to simplify the prerequisites so another JDK does not need to be installed.

2. Download GraalVM, unzip the archive, export the GraalVM home directory as the $JAVA_HOME and add $JAVA_HOME/bin to the PATH environment variable: On Linux:

  export JAVA_HOME=/home/${current_user}/path/to/graalvm
  export PATH=$JAVA_HOME/bin:$PATH

On macOS:

  export JAVA_HOME=/Users/${current_user}/path/to/graalvm/Contents/Home
  export PATH=$JAVA_HOME/bin:$PATH

On Windows:

  setx /M JAVA_HOME "C:\Progra~1\Java\<graalvm>"
  setx /M PATH "C:\Progra~1\Java\<graalvm>\bin;%PATH%"

Note that your paths are likely to be different depending on the download location.

3. Install Native Image by running.

  gu install native-image

4. Then execute:

  ./build.sh

Have a look at the build.sh script which creates a native executable from a Java class. The native-image utility compiles the application ahead-of-time for faster startup and lower general overhead at runtime.

$JAVA_HOME/bin/native-image --no-fallback -cp ./target/mixed-code-hello-world-1.0-SNAPSHOT-jar-with-dependencies.jar -H:Name=helloworld -H:Class=hello.JavaHello -H:+ReportUnsupportedElementsAtRuntime

It takes a few parameters: the classpath, the main class of the application with -H:Class=..., and the name of the resulting executable with -H:Name=....

After executing the native-image command, check the directory. It should have produced the executable file, helloworld.

Running the Application #

To run the application, you need to execute the JAR file in the target directory. You can run it as a normal Java application using java. Or, since we have a native executable prepared, you can run that directly. The run.sh file executes both, and times them with the time utility:

java -cp ./target/mixed-code-hello-world-1.0-SNAPSHOT-jar-with-dependencies.jar hello.JavaHello
./helloworld

An output close to the following should be produced:

→ ./run.sh
+ java -cp ./target/mixed-code-hello-world-1.0-SNAPSHOT-jar-with-dependencies.jar hello.JavaHello
Hello from Kotlin!
Hello from Java!

real	0m0.129s
user	0m0.094s
sys	0m0.034s
+ ./helloworld
Hello from Kotlin!
Hello from Java!

real	0m0.010s
user	0m0.003s
sys	0m0.004s

The performance gain of the native version is largely due to the faster startup.

License #

This sample application is taken from the JetBrains Kotlin-examples repository. It is distributed under the Apache License 2.0.

Connect with us