There has been a lot of interest in the past few years about "native Java". You've probably read articles or seen conference talks on the subject.
Traditionally Java programs are compiled to bytecode. At runtime the bytecode is first interpreted and eventually compiled to native machine code by a JVM.
Native Java is the idea of using ahead-of-time (AOT) compilation to produce a native executable or "native image" for a Java application.
A native image can run standalone without relying on a JVM.
This approach potentially offers advantages in terms of:
GraalVM is an open source JDK that can compile Java applications to native images, as well as bytecode in the traditional way.
Spring is the most popular Java application framework used today.
Over the last three years, the Spring and GraalVM teams have been working to make it easier for developers to deliver their Spring applications as native images.
Experimental support for native image was achieved via the Spring Native project.
With Spring Boot 3 and Spring 6 (due to be released November 24, 2022), support for native image will be available as a core feature.
Since the release candidate for Spring Boot 3 is now available, this seems a good time to try out the native image support.
So lets have a go at using the Spring Boot 3 release candidate and GraalVM to create a simple web application, compiled to a native executable.
You need to have GraalVM and it's native-image
tool installed on your machine.
You can download these from here.
Note that you need GraalVM 22.3.0 and the equivalent version of native-image
.
I'm using GraalVM Enterprise and native-image
22.3.0
The easiest way to get started with building a Spring Boot app is to use the spring initializr.
I'm going to walk through using both Maven and Gradle, so you can pick whichever one you pefer.
You can either enter the configuration by hand, or re use one of:
After choosing the build engine, I selected:
Once you are happy with your configuration click the "GENERATE" button.
Then copy the downloaded file to a directory of your choice, unzip it and cd
into the vanilla
directory.
Start by running the application:
./gradlew bootRun
The application should start in a second or so:
You can to test the application:
curl localhost:8080
404
isn't a good look, so let's add a RestController
to say something more meaningful.
Copy Hello.java from the code directory into ./src/main/java/com/example/vanilla
(or write your own).
If we run the application (./gradlew bootRun
) and test it (curl localhost:8080
) again we should see a more meaningful response this time:
We can also build a stand alone jar:
./gradlew assemble
And run that:
java -jar ./build/libs/vanilla-0.0.1-SNAPSHOT.jar
Again you should see the app start in about a second:
So now we can take the next step and build our native image without having to make any changes to the project:
./gradlew nativeCompile
This may be a good opportunity to have a tea or coffee as it will take two or three minutes as opposed to a couple of seconds for building a jar (on my Mac, YMMV).
Once the build completes:
We can run the executable:
./build/native/nativeCompile/vanilla
You should see the application start in around ~0.1 seconds (so about 10x faster).
Looking at memory usage on my machine, the native image
uses about 1/3 of the memory compared to running the application from the jar
.
Before you start, make sure that $JAVA_HOME
is set to point to your GraalVM installation:
$ $JAVA_HOME/bin/java --version
java 17.0.5 2022-10-18 LTS
Java(TM) SE Runtime Environment GraalVM EE 22.3.0 (build 17.0.5+9-LTS-jvmci-22.3-b07)
Java HotSpot(TM) 64-Bit Server VM GraalVM EE 22.3.0 (build 17.0.5+9-LTS-jvmci-22.3-b07, mixed mode, sharing)
Then run the application:
./mvnw spring-boot:run
The application should start in a second or so:
You can use curl localhost:8080
to test the application:
404
isn't a good look, so let's add a RestController
to say something more meaningful.
Copy Hello.java from the code directory into ./src/main/java/com/example/vanilla
(or write your own).
If we run the application (./mvnw spring-boot:run
) and test it (curl localhost:8080
) again we should see a more meaningful response this time:
We can also build a stand alone jar:
/.mvnw package
And run that:
java -jar ./target/vanilla-0.0.1-SNAPSHOT.jar
Again you should see the app start in about a second.
So now we can take the next step and build our native image without having to make any changes to the project:
./mvnw -Pnative native:compile
This may be a good opportunity as it will take two or three minutes as opposed to a couple of seconds for building a jar (on my Mac, YMMV).
Once the build completes:
We can run the executable ./target/vanilla
You should see the application start in around ~0.1 seconds (so about 10x faster).
Looking at memory usage on my machine, the native image
uses about 1/3 of the memory compared to running the application from the jar
.
So in summary, with Spring Boot 3.0, you can now easily leverage GraalVM Native image to build your applications as native executables.
Enjoy your faster startup, lower latency and lower infrastructure costs.
Ewan Slater is the Technical Director of Java & GraalVM product marketing at Oracle with over twenty years experience in the technology industry.
Ewan started programming in Java in 1997 and joined Oracle with the acquisition of Thor Technology in 2005.
His current focus is on helping Oracle’s customers and partners understand the technical benefits of Java and GraalVM.
He is involved with a number of open source projects, is a recovering conference organiser and a member of the Ipswich & Suffolk Technology Network (ISTN).
Outside of work, he can usually be found outdoors in the Suffolk countryside, or indoors in the kitchen.