GraalVM is a high performance Java Development Kit (JDK). It's built on the same foundation as traditional OpenJDK setup, using the powerful HotSpot Virtual Machine (VM) underneath.
First off, you can run your Java programs on GraalVM just like you normally would. You don't have to change anything in your code. All your usual tools and libraries work just fine. The only difference is that GraalVM uses a really smart compiler called the Graal Compiler, which is written in Java itself.
The Graal Compiler is a dynamic just-in-time (JIT) compiler, written in Java, that converts bytecode into machine code. Unlike traditional JIT compilers (like C1 and C2 in HotSpot), which are usually written in lower-level languages such as C++, Graal stands out by being implemented in Java itself.
But here's where it gets really cool: GraalVM can also turn your Java programs into standalone applications that don't need JDK to run. It compiles your code into a special kind of file called a native executable. So instead of needing Java installed on your computer, you can just run the executable like any other program. This not only makes your programs run faster, but it also makes them easier to distribute and use.
GraalVM adds an advanced just-in-time (JIT) optimizing compiler, which is written in Java, to the HotSpot Java Virtual Machine.
Moreover, GraalVM has a special framework called Truffle, which allows it to run not only Java but also other languages like JavaScript, Ruby, and Python. This means you can mix and match different languages within the same program, making it easier for them to work together seamlessly.
Some impressive features of Graal VM:
Quick Start-Up: GraalVM can compile your Java applications into standalone programs before running them. These programs are smaller, start up to 100 times faster, and use less memory and CPU compared to those running on a regular JVM.
Increased Security: By cutting out unused code and limiting certain dynamic Java features to the build time, GraalVM makes your applications more secure and less prone to attacks.
Supports Multiple Languages: GraalVM isn’t just for Java. It also supports JavaScript, Ruby, Python, and more, so you can run applications that use different languages all together.
Better Performance: GraalVM improves how your apps run by reducing delays, enhancing peak performance, and shortening garbage collection time.
Perfect for Cloud and Microservices: GraalVM works well with popular microservices frameworks like Spring Boot, Micronaut, Helidon, and Quarkus. It’s also compatible with major cloud platforms like Oracle Cloud, AWS, Google Cloud, and Microsoft Azure.
Two Versions of Graal VM are available:
💡 Important: In this article, we will try to give you a good understanding to get you up and running with Graal VM but Graal VM is too vast to cover everything in a single article. Each feature of GraalVM is so interesting that it could have its own article! If you find something about GraalVM that catches your eye, feel free to dive deeper and explore. And the best part? GraalVM is open-source, so you can tinker with it and learn more at your own pace.
Early Beginnings (2010s)
GraalVM 0.1 (2014)
GraalVM 1.0 (2017)
Acquisition by Oracle (2018)
GraalVM 19.0 (2019)
Today
GraalVM offers multiple ways to run your Java applications, whether you prefer the traditional JVM method, or need faster startup and lower memory usage with native executables, or want to incorporate multiple programming languages into your project.
💡 Ahead of Time (AOT) compilation involves a tradeoff: it requires additional setup during the build process and offers less flexibility during runtime.
Native Image:
💡 GraalVM introduces a feature called native image, which is a new concept in the Java ecosystem. It allows you to compile your Java application ahead of time into a standalone executable file.
A native image is a special version of your Java application that is compiled before you run it, rather than while it's running. This process is called Ahead-Of-Time (AOT) compilation.
The process of creating a native executable begins with the Native Image builder, also known as native-image. This tool takes your application's classes and metadata and transforms them into a binary file suitable for a specific operating system and architecture.
Here's how it works:
This entire process, known as "build time," is separate from the usual compilation of Java source code into bytecode. It ensures that the resulting native executable is finely tuned for performance and optimized for the target environment.
The native-image tool transforms Java bytecode into a native executable. You can create this executable from various sources:
By converting these Java bytecode formats into native executables, the native-image tool enables your applications to run directly on your operating system, bypassing the need for the JVM and thereby enhancing performance and reducing startup times.
What happens(In Brief):
What you get:
💡 Heap Snapshotting is another big topic which we will try to cover in detail in another article.
We have written a Java program to test its performance, focusing on peak execution time. The program will be run in three different environments:
By comparing the execution times in these environments, we will see how each setup affects the performance of the Java program.
Main.java
TaskRunner.java
SimpleText.java
Explanation for the above code:
This code measures how long it takes to perform a bunch of simple tasks.
Basically, this code tests how quickly it can do simple tasks and gives us the time it took as a performance measure.
Output using Windows PowerShell:
Output:
It tooks 25 milliseconds to do the computation with Amazon Corretto OpenJDK.
Output using Windows PowerShell:
Output:
It took 25 milliseconds to complete the computation using Amazon Corretto OpenJDK. Where as GraalVM JDK completed the same computation in 22 milliseconds, which is at peak.
Before building and running our native executable file using GraalVM, we need to set up some essential software.
Configuring GraalVM on a machine to utilize AOT native image capabilities can be quite complex. Since, i am using windows for this writeup, therefore, I have made an effort to simplify this process for Windows users:
1. Prerequisites for Native Image on Windows
To use native image on windows, one must have visual studio downloaded in their machine with Microsoft Visual C++(MSVC). On Windows, Native Image requires Visual Studio and Microsoft Visual C++(MSVC). Use Visual Studio 2022 version 17.6.0 or later.
2. Install Visual Studio Build Tools and Windows SDK
💡 After installing the necessary tools, you should now have the capability to compile with GraalVM Native Image.
3. Checking Existing Visual Studio Installations
If Visual Studio is already installed on your system, follow these steps to check that the correct components are installed.
Now, once Visual Studio is setup in your machine,we will check the performance with native image also and compare it with graal vm open jdk and amazon corretto open jdk.
One of the cool features of Graal VM is Native Image, which compiles your Java code into a standalone executable binary. This means you can run your Java apps without needing a full Java runtime environment.
Now, why do we need Visual Studio for this?
Well, on Windows, Native Image relies on some components from Visual Studio. Here's why:
Native Image and Windows:
These tools help GraalVM work its magic and create that efficient binary.
How to build native image of our code:
In our code, we begin by compiling the Main.java file to produce the essential Main.class file. This file contains the bytecode representation of our Java code. Next, we use the power of GraalVM OpenJDK to transform this Main.class file into a native executable i.e. main.exe file. This is achieved with a straightforward command: native-image Main
.
💡 Note: run the above command inside the folder containing these files.The .exe files generated by GraalVM Native Image are platform-specific because they are optimized to run efficiently on a particular operating system, CPU architecture, and runtime environment. This ensures maximum performance, compatibility, and seamless integration with the target platform.
Once this conversion process is completed, we are left with a platform-specific .exe file ready to be executed. We launch our newly created executable using the ./main command in the terminal. Following this command, the program springs to life, swiftly executing our code.
In our case, the execution time clocks in at a remarkable 16 milliseconds, demonstrating the efficiency of our native executable."
Windows PowerShell:
💡 Observation:
From the above results,One can observe that using the AOT-compiled native image takes 16 ms, while using the GraalVM OpenJDK takes 40 seconds, which on peak performance could be reduced to 22 seconds. This represents a significant performance difference.
Some might think the gap isn't a big deal, but it really matters, especially in big projects. Even small differences in how long something takes to run can add up and cause significant delays, especially in large-scale projects or when you need things to happen quickly.
This highlights why it's important to think carefully about performance, like whether to use AOT compilation or stick with traditional JVM execution, especially when speed and efficiency are important.
Java programs typically run faster on the GraalVM JDK compared to the native Java JDK because GraalVM includes advanced Just-In-Time (JIT) compilation techniques and optimizations. However, there are specific scenarios where the native Java JDK might outperform GraalVM, especially in cases where GraalVM's advanced optimizations do not provide a significant benefit or even introduce overhead.
The native image typically has faster startup times and lower memory usage but might sometimes be slower for long-running, computationally intensive tasks due to the lack of JIT optimizations.
In this chapter, we'll explore how to leverage GraalVM to build a native executable from a JAR file, specifically focusing on developing a Spring Boot web application.
GraalVM works well with popular microservices frameworks like Spring Boot, Micronaut, Helidon, and Quarkus.
Spring Boot is a widely-used framework for building web applications and microservices due to its ease of use and extensive ecosystem. Integrating Spring Boot with GraalVM can unlock benefits such as faster startup times, reduced memory consumption, and improved overall performance.
Goal:
We'll create a basic Spring Boot web application containing just one REST endpoint. Then, we'll compare how long it takes for the application to start up using the traditional java -jar
command and with a native executable.
To begin, go to start.spring.io and generate a Maven Spring Boot project with the following dependencies:
Our code should look something like this:
HomeController.java
Explanation for the above code: This code sets up a simple web server using Spring Boot. When you visit the "/hello" URL in a web browser, it responds with the message "Hello Native Image from SpringBoot Jar".
💡 Note: I already have Maven installed on my system, and it's essential to use the GraalVM JDK to build the native executable from the Spring Boot jar file.
💡 Note: To make a native executable, we first need to create a jar file for our Spring Boot app. We do this by running the command mvn clean package
. This command compiles and packages our app into a single file that contains all the necessary code and resources.
Creating a jar file:
Windows PowerShell:
Building our native executable:
This command mvn -Pnative native:compile -DskipTests
instructs Maven to:
native:compile
) from the Spring Boot project.Pnative
flag activates the Maven profile configured for native compilation.DskipTests
flag skips running any tests during the compilation process.
Windows PowerShell:
It took 4 minutes 46 seconds to build the native executable using jar file. This was the trade off i was previously talking i.e. build time.This build time can vary depending on the hardware specifications of the system, highlighting the trade-off between build time and system performance. Because my laptop is a bit older, the build process took longer. Normally, it should take around 2 minutes, which is sufficient for the build.
After running the command, a .exe file i.e. native executable is generated as shown in the below image.
Now let’s see, how fast our Spring Boot application starts up by running the jar file with the usual java -jar fileName
command.
Windows PowerShell:
Our SpringBoot application started in 2.747 seconds.
Executing our native exectuable:
Now, we'll run our native executable and check how quickly our application starts up.
Conclusion:
When we ran the native image of the Spring Boot jar file, it started up in just 0.078 seconds. However, if we run the jar file directly using java -jar
from the command prompt, it takes 2.747 seconds. This time difference tends to get bigger as our application gets larger. But it's worth noting that building the native image takes longer, so there's a trade-off to consider.
We are not going into detail of these.Feel free to explore if you find them intriguing.
Polyglot Capabilities:
Additional Ongoing Graal Projects:
💡 Please understand that it's not possible to cover everything in just one article, so there will be more articles on this topic coming your way soon.
Cheers!
Happy Coding...