Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Native build

A normal Java application runs on the JVM: the virtual machine loads classes, warms up and only then runs fast. A native build (native image) turns the application into a plain executable for a particular operating system — like a program in C or Go. No JVM is needed to run it. The build is done by the native-image tool from GraalVM or its Quarkus distribution, Mandrel. Quarkus was designed for native builds from the start, so most extensions work there without extra effort.

A jar or a native executable

Jar on the JVMNative executable
StartupSecondsTens of milliseconds
Memory after startupHundreds of MBTens of MB
Build timeTens of secondsSeveral minutes
Peak speed under loadHigher thanks to the JITUsually lower
RunningNeeds JavaNeeds only the file

A native executable pays off where a service starts and stops often: serverless functions, autoscaling in Kubernetes, small containers. For a long-running service under constant load, a normal jar is no worse and often better.

Building with GraalVM installed

Install GraalVM for Java 25 (or Mandrel) and put its path into the GRAALVM_HOME variable. The generator has already added the native profile to pom.xml, so the command is short:

./mvnw package -Dnative

The build takes several minutes and needs a lot of memory — 8 GB of free RAM is best. The result is the target/blog-1.0.0-SNAPSHOT-runner file (with an .exe extension on Windows), which you run directly:

./target/blog-1.0.0-SNAPSHOT-runner

The startup line says native instead of on JVM, and the startup time is in milliseconds.

Building in a container

If you do not want to install GraalVM but have Docker or Podman, Quarkus builds the file inside a container with Mandrel:

./mvnw package -Dnative -Dquarkus.native.container-build=true

Note that this produces an executable for Linux. It fits a Docker image (the next lesson) but does not run directly on Windows or macOS.

Limits

native-image analyses in advance which code is reachable and throws the rest away. So what Java normally does at run time needs hints in a native executable:

  • Reflection. Classes your code creates or reads through reflection are marked with @RegisterForReflection. Quarkus registers entities, resources and JSON records itself.
  • Resources. Files from src/main/resources that you read by hand are listed in quarkus.native.resources.includes.
  • Libraries. Use Quarkus extensions instead of bare libraries: an extension already contains the settings the native build needs.

A native build is checked with @QuarkusIntegrationTest tests: the generator created GreetingResourceIT, and following it you can write PostResourceIT extends PostResourceTest. The ./mvnw verify -Dnative command builds the executable and runs the tests right against it.

Common mistake

If you run -Dnative without GraalVM, the build fails at the very end:

[ERROR] Failed to execute goal io.quarkus.platform:quarkus-maven-plugin:3.40.1:build (default-build) on project blog:
Failed to build quarkus application: ... Build step io.quarkus.deployment.pkg.steps.NativeImageBuildStep#build threw an exception:
java.lang.RuntimeException: Cannot find the `native-image.cmd` in the GRAALVM_HOME, JAVA_HOME and System PATH.

This message comes from Windows, where the tool is called native-image.cmd. Install GraalVM or Mandrel and set GRAALVM_HOME, or build in a container with -Dquarkus.native.container-build=true. On Windows native-image also needs the Visual Studio build tools.

Thanks for reading!

Donate to the site — we work hard for you.