GraalVM Native Images

This guide describes how to build a GraalVM native image for a Helidon SE application.

Introduction

Native images are ahead-of-time compiled Java code that result in a self contained native executable. When used appropriately native images have dramatically faster startup and lower runtime memory overhead compared to a Java VM.

In this guide you will learn how to build a native image locally on your machine, as well as using Docker.

What You Need

About 10 minutes
Helidon Prerequisites
GraalVM CE 21.3.0

Install GraalVM and the Native Image Command

After downloading and installing GraalVM, set the GRAALVM_HOME environment variable to point at your GraalVM installation.

# Your path might be different
export GRAALVM_HOME=/usr/local/graalvm-ce-java11-21.3.0/Contents/Home
Copied

Then install the optional native-image command:

$GRAALVM_HOME/bin/gu install native-image
Copied

And verify:

$GRAALVM_HOME/bin/java -version
$GRAALVM_HOME/bin/native-image --version
Copied

Generate The Project

Generate the project using the Helidon SE Quickstart Maven archetype.

mvn archetype:generate -DinteractiveMode=false \
    -DarchetypeGroupId=io.helidon.archetypes \
    -DarchetypeArtifactId=helidon-quickstart-se \
    -DarchetypeVersion=1.4.12 \
    -DgroupId=io.helidon.examples \
    -DartifactId=helidon-quickstart-se \
    -Dpackage=io.helidon.examples.quickstart.se
Copied

The archetype generates a Maven project in your current directory (for example, helidon-quickstart-se). Change into this directory and build.

cd helidon-quickstart-se
mvn package
Copied

At this point you can run the application using the JVM:

java -jar target/helidon-quickstart-se.jar
Copied

In another shell test an endpoint:

curl -X GET http://localhost:8080/greet
Copied

The application should respond with {"message":"Hello World!"}

Now stop the running application (by pressing Ctrl+C).

For more information about the Quickstart application and other enpoints it supports see the Helidon SE Quickstart Guide.

Building a Native Image

You can build a native executable in 2 different ways:

  • With a local installation of GraalVM

  • Using Docker

Local build

Make sure you have GraalVM locally installed:

$GRAALVM_HOME/bin/native-image --version
Copied

Build the native image using the native image profile:

mvn package -Pnative-image
Copied

Tip

This uses the helidon-maven-plugin to perform the native compilation using your installed copy of GraalVM. It might take a little while to complete.

Once it completes start the application using the native executable (no JVM!):

./target/helidon-quickstart-se
Copied

Yep, it starts fast. You can exercise the application’s endpoints as before.

Multi-stage Docker build

Build the "native" Docker Image

docker build -t helidon-quickstart-se-native -f Dockerfile.native .
Copied

Tip

This does a full build inside the Docker container. The first time you run it, it will take a while because it is downloading all of the Maven dependencies and caching them in a Docker layer. Subsequent builds will be much faster as long as you don’t change the pom.xml file. If the pom is modified then the dependencies will be re-downloaded.

Start the application:

docker run --rm -p 8080:8080 helidon-quickstart-se-native:latest
Copied

Again, it starts fast. You can exercise the application’s endpoints as before.

How small and fast is this?

First let’s take a look at the Docker image size:

docker images helidon-quickstart-se-native:latest
Copied
REPOSITORY             TAG       IMAGE ID       CREATED        SIZE
quickstart-se-native   latest    1227ac82d199   5 days ago     21.4MB

That’s much smaller than a Docker image with the application code plus the JRE and operating system files needed to run it.

Startup times are quite impressive. These are just some informal numbers to show the magnitude of speed-up. Your mileage may vary:

Helidon SE Quickstart Java VM0.921 seconds
Helidon SE Quickstart Native0.026 seconds

The startup time for Helidon SE with the normal Java VM is quite good at under a second. But the native executable is an order of magnitude faster. Memory footprint is similarly improved.

When should I use Native Images?

Native images are ideal for applications with high horizontal scalability requirements where the ability to rapidly scale out to numerous instances is important.

That said, native images do have some limitations, and for long running applications where startup and footprint are less of a priority, the Java SE HotSpot VM might be more appropriate.

What about Helidon MP?

Currently GraalVM native image support is only available for Helidon SE.