GraalVM Native Image
GraalVM is a high-performance JDK distribution that compiles Java applications into standalone, native binaries, ahead-of-time compiling Java to native machine code. These binaries start instantly, do not need to go through a warm-up phase, use fewer resources and scale very well. GraalVM may be used like any other Java Development Kit in the IDE of your choice.
While GraalVM also offers a JIT-based runtime and rich polyglot capabilities through the Truffle framework, it’s native image capability is the elephant in the room. GraalVM’s binary packages include a native-image binary. This is the tool used to compile Java applications into native executables.
What GraalVM offers
GraalVM ahead-of-time compiles all the application, and standard library, classes that are encountered in a static analysis of the application. Static members of classes may be initialized at compile time. This is a significant departure from Java’s traditional lazy loading of classes. And it makes Java reflection convoluted while running with GraalVM.
There is no dynamic Just-In-Time compiler. So, there is no runtime profiling and optimization. Also, very limited garbage collection options are offered. The community edition offers serial GC
only. Oracle’s GraalVM Enterprise Edition additionally offers G1 GC
. It is possible to completely disable garbage collection using the epsilon GC
option, with both the editions. Limited garbage collection and no JIT compiler save a significant number of CPU cycles that the application is free to use.
In short, GraalVM trades some of Java’s dynamic/runtime features for an improved application startup performance and a more efficient use of the available resources.
GraalVM Community Edition for Ubuntu
GraalVM Community Edition is now available and supported on Ubuntu 24.04 and above, through a snap package.
snap install graalvm-jdk
The snap offers the latest GraalVM version through the latest/stable
channel, the development version on latest/edge
and GraalVM for JDK 21 on the v21
channel. So, to install GraalVM for JDK 21 run:
snap install graalvm-jdk --channel=v21
Please report issues here.
Native compilation of Spring Boot applications
Spring Boot 3+ provides official support for compiling Java applications to native executables. The official documentation is found here.
To support native compilation with a maven project, a new plugin declaration needs to be added to the pom.xml:
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
</plugin>
For a Gradle project, add the following plugin declaration to the plugins block in the build.gradle:
id 'org.graalvm.buildtools.native' version '0.10.6'
Next, install the GraalVM Community Edition snap from the snap store.
sudo snap install graalvm-jdk --channel=v21
Point the JAVA_HOME environment variable to the GraalVM CE installation.
export JAVA_HOME=/snap/graalvm-jdk/current/graalvm-ce/
And finally, native compile the maven project:
./mvnw -Pnative native:compile
Or, for your Gradle project run:
./gradlew nativeCompile
The last step would first build the application using the typical maven/gradle workflow and subsequently invoke GraalVM native-image
to compile it into a native executable. For a maven project, the native executable will be created under the target
directory in the project. The application may be launched by simply executing this file on the command line.
Example: Native compile of the Spring PetClinic application
Based on steps mentioned in the previous section, here is a demo video showing the native compilation of the Spring PetClinic sample application.
This 6-min video is a simple screen-cast and does not have any voice-over. Please bear with me!
Does every Java application benefit from native compilation?
The short answer is, no!
As noted earlier, GraalVM trades some of Java’s dynamic behaviors for startup performance and better scalability of Java applications. Consequently, applications that do not depend on those compromised behaviors stand to benefit from GraalVM.
Typically, long-running applications running on the JVM see significant benefits of JIT compilation, runtime profiling and re-optimization, over time - popularly referred to as warm-up. Long-running applications also need mature and fine-tuned garbage collection strategies. In contrast, GraalVM benefits short-lived applications that need to start quickly that do not really need memory management!
For fast-startup of long-running Java applications, OpenJDK CRaC (Coordinated Restore at Checkpoint) offers a more promising solution. I hope to delve into that in a future blog-post.