Blog
navigate_next
Java
Guide to Graal VM
Gaurav Sharma
May 30, 2024

Chapter 0: Introduction→What is Graal VM?

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
:

  • Oracle GraalVM: This version comes with 24/7 support from Oracle and is licensed for commercial use.
  • GraalVM Community Edition: This open-source version is available under the GNU General Public License.We will be using Community Edition in this article.

💡 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.

An image depicting all the capabilities of Graal VM

Brief History:


Early Beginnings (2010s)

  • The GraalVM project was initiated by Oracle Labs in the early 2010s as a research project to explore new ways of executing Java bytecode.
  • The project was led by Thomas Würthinger, a researcher at Oracle Labs, who had a vision to create a high-performance, multi-language virtual machine.

GraalVM 0.1 (2014)

  • The first public release of GraalVM, version 0.1, was announced in 2014. This initial release focused on executing Java bytecode and introduced the concept of a "native image" - a ahead-of-time compiled representation of Java code.

GraalVM 1.0 (2017)

  • In 2017, GraalVM 1.0 was released, which added support for additional languages, including JavaScript, Ruby, and R. This release also introduced the GraalVM compiler, which enabled ahead-of-time compilation of Java code.

Acquisition by Oracle (2018)

  • In 2018, Oracle acquired the GraalVM project and its team, further solidifying its commitment to the technology.

GraalVM 19.0 (2019)

  • The release of GraalVM 19.0 in 2019 marked a significant milestone, as it introduced support for running native images as standalone executables, without the need for a JVM.

Today

  • GraalVM is now a widely adopted technology, used in production environments by companies such as Alibaba, and Oracle.
  • The project continues to evolve, with ongoing research and development focused on improving performance, security, and language support.

Benefits it provides:

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.

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.

Traditional Java Virtual Machine (JVM) Mode:

  • GraalVM can run your Java applications just like a regular Java Virtual Machine (JVM). This means you can compile your Java code into bytecode and run it on the JVM, benefiting from features like Just-In-Time (JIT) compilation, which optimizes your code while it runs.

Native Image Mode:

  • GraalVM can also compile your Java applications ahead of time into native executables. This is called an “Ahead-Of-Time (AOT)” compilation. These native executables start up faster and use less memory because they don’t need the JVM to run. This is great for creating lightweight and fast applications, especially useful for cloud services or applications with high-performance needs.

💡 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.

How Native Image Works:

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:

  1. Static Analysis: First, the native-image tool examines your code to determine which classes and methods your application will actually use when it runs.
  2. Compilation: After analyzing your code, the tool compiles the identified classes, methods, and resources into a binary format. Instead of just compiling your Java code into bytecode (which runs on the JVM), GraalVM compiles your application into a native executable. This executable can run directly on your operating system, just like any other native program.

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.

Performance Benefits:

  • Fast Startup: Native images start up much faster than traditional Java applications because they don't need to initialize and load the JVM.
  • Low Memory Usage: These native images use less memory because they don't need the Just-In-Time (JIT) compilation process that the JVM uses. Without the need to compile code at runtime, there's less overhead and lower memory consumption.
  • No Warmup: Native images are ready to perform immediately upon startup, unlike traditional JVM applications that require a warmup period to reach peak performance.
  • Reduced Attack Surface: By excluding unused code and limiting dynamic features to build time, native images are more secure and less vulnerable to attacks.
  • Compact Packaging: Native images are smaller in size, making them easier to distribute and deploy.
  • Lower Compute Costs: Due to their efficient use of resources and faster startup times, native images can lower compute costs, especially in cloud environments where resources are billed based on usage.
A diagram enlisting multiple parameters and the effect of native compilation on them

Building a Native Executable

The native-image tool transforms Java bytecode into a native executable. You can create this executable from various sources:

  • Class File: You can generate a native executable directly from a compiled Java class file.
  • JAR File: The tool can also take a JAR file, which bundles multiple class files, as input to create a native executable.
  • Module: For applications using Java 9 or higher, you can build a native executable from a module, allowing you to leverage the modular system introduced in Java 9.

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.

Native Image Build Process:

A diagram illustrating a native image build process from Input, and Iterative analysis, to the native executable output

What happens(In Brief):

  1. Input Gathering: The process begins by collecting all the classes from your application, its libraries, and the Java Development Kit (JDK).
  2. Points-to Analysis: This step involves a technique called points-to analysis. It's like a deep dive into the code to understand how data flows and which pointers (references to memory locations) might point to which objects. This helps ensure memory safety and optimize the memory layout.
  3. Iterative Analysis: The points-to analysis isn't a one-time thing. It keeps running in a loop, refining its understanding of the code until no new information is discovered. This steady state is known as reaching a fixed point.
  4. Creating the Image Heap: Using the information from the points-to analysis, an image heap is created. Think of this as a snapshot of your application's memory at a specific point, ready to be used for further processing.
  5. Ahead-of-Time (AOT) Compilation: Next, the image heap is used for ahead-of-time compilation. This means that the code is compiled into a native executable file, ready to run directly on the operating system without needing a Java Virtual Machine (JVM).
  6. Text Section of the Executable: The compiled code is placed in the text section of the executable file. This section is read-only and contains all the instructions the CPU will execute.
  7. Including Initializations: Any initializations that your application needs are also included in the executable. This ensures that when you run the executable, it starts up correctly with all necessary setups.
  8. Substrate VM: The Substrate VM, a lightweight virtual machine tailored for running these native executables, loads the image heap. This VM provides the necessary environment for your application to run efficiently.
  9. Heap Snapshotting: The image heap is then snapshotted, meaning it's saved to disk. This snapshot allows for quick restarts because instead of rebuilding the heap from scratch, it can be reloaded from this saved state.
  10. Writing the Image Heap: Finally, the image heap is written to the data section of the native executable file. The data section is a read-write memory area containing all the necessary data for the application to function.

What you get:

  • A native executable file that can be run directly on the target platform, without the need for a JVM or dynamic compilation.

💡 Heap Snapshotting is another big topic which we will try to cover in detail in another article.

Banner to that takes you to the latest version of the Unlogged plugin on the Jetbrains marketplace

Chapter 1: Comparing Java Code Performance: Open JDK, GraalVM JDK(Own JIT Compiler), and Native Image

We have written a Java program to test its performance, focusing on peak execution time. The program will be run in three different environments:

  1. Open JDK - The standard Java Development Kit provided by Oracle or Amazon Corretto.
  2. GraalVM (JIT) JDK - Utilizing GraalVM's high-performance Just-In-Time compiler.
  3. Graal VM Native Image - Compiling the Java code ahead of time into a native executable using GraalVM.

By comparing the execution times in these environments, we will see how each setup affects the performance of the Java program.

Main.java

	
public class Main {
    public static void main(String[] args) {
        long startTime = System.nanoTime();

        TaskRunner runner = new TaskRunner();
        int result = runner.runTasks(10000000);

        long endTime = System.nanoTime();
        long duration = (endTime - startTime) / 1_000_000; 

        System.out.println("Result: " + result);
        System.out.println("Execution time: " + duration + " ms");
    }
}

TaskRunner.java

	
public class TaskRunner {
    public int runTasks(int numberOfTasks) {
        int result = 0;
        for (int i = 0; i < numberOfTasks; i++) {
            SimpleTask task = new SimpleTask(i);
            result += task.compute();
        }
        return result;
    }
}

SimpleText.java

	
public class SimpleTask {
    private int value;

    public SimpleTask(int value) {
        this.value = value;
    }

    public int compute() {
    
        return (value * 31) % 7;
    }
}

Explanation for the above code:

This code measures how long it takes to perform a bunch of simple tasks.

  • In the Main part, it starts counting time, does the tasks, then stops counting time. It shows how long it took to do the tasks.
  • The TaskRunner part does the actual tasks. It gets told how many tasks to do. For each task, it does a simple calculation and adds up the results.
  • Each SimpleTask is a single thing to do. It just does a simple math problem.

Basically, this code tests how quickly it can do simple tasks and gives us the time it took as a performance measure.

Chapter 1.1: Checking the performance with Amazon Corretto Open JDK:

Output using Windows PowerShell:

	
PS D:\PerformanceTest\src> java -version
openjdk version "22.0.1" 2024-04-16
OpenJDK Runtime Environment Corretto-22.0.1.8.1 (build 22.0.1+8-FR)
OpenJDK 64-Bit Server VM Corretto-22.0.1.8.1 (build 22.0.1+8-FR, mixed mode, sharing)
PS D:\PerformanceTest\src> ls


    Directory: D:\PerformanceTest\src


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----         5/28/2024   1:19 AM            472 Main.java
-a----         5/28/2024   1:14 AM            276 SimpleTask.java
-a----         5/28/2024   1:14 AM            283 TaskRunner.java


PS D:\PerformanceTest\src> javac Main.java
PS D:\PerformanceTest\src> ls


    Directory: D:\PerformanceTest\src


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----         5/28/2024   3:49 AM           1067 Main.class
-a----         5/28/2024   1:19 AM            472 Main.java
-a----         5/28/2024   3:49 AM            309 SimpleTask.class
-a----         5/28/2024   1:14 AM            276 SimpleTask.java
-a----         5/28/2024   3:49 AM            396 TaskRunner.class
-a----         5/28/2024   1:14 AM            283 TaskRunner.java


PS D:\PerformanceTest\src> java Main
Result: 30000000
Execution time: 25 ms
PS D:\PerformanceTest\src>

Output:

It tooks 25 milliseconds to do the computation with Amazon Corretto OpenJDK.

Chapter 1.2: Checking the performance with GraalVM JDK with High Performance JIT Compiler:

Output using Windows PowerShell:

	
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

Install the latest PowerShell for new features and improvements! https://aka.ms/PSWindows

PS D:\PerformanceTest\src> java -version
openjdk version "22.0.1" 2024-04-16
OpenJDK Runtime Environment GraalVM CE 22.0.1+8.1 (build 22.0.1+8-jvmci-b01)
OpenJDK 64-Bit Server VM GraalVM CE 22.0.1+8.1 (build 22.0.1+8-jvmci-b01, mixed mode, sharing)
PS D:\PerformanceTest\src> java Main
Result: 30000000
Execution time: 25 ms
PS D:\PerformanceTest\src> java Main
Result: 30000000
Execution time: 22 ms
PS D:\PerformanceTest\src>

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.

Chapter 1.3: Checking the performance using GraalVM Native Image(AOT)→Building a native executable from a compiled class file

Before building and running our native executable file using GraalVM, we need to set up some essential software.

Setup on Windows Platforms

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

  • Download the Visual Studio Build Tools 2022 or later (C development environment) from visualstudio.microsoft.com
  • Start the installation by opening the file you downloaded, and then click Continue:
Visual Studio installer dialogue box
  • Select the Desktop development with C++ checkbox in the main window. Once you select this, a window on the right side will appear under Installation Details, make sure that the two requirements, Windows 11 SDK and MSVC (…) C++ x64/x86 build tools, are selected. Continue by clicking Install.
Snapshot of visual studio installation configuration step

💡 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.

  • Open the Visual Studio Installer from search menu in your computer:
Step 1 of checking for existing visual studio installations
  • Under the Installed tab, click Modify and choose Individual Components:
  • Then scroll to the bottom and confirm that the Windows 11 SDK and Visual Studio SDK checkboxes are selected. Now you can start using Native Image.
Confirming that the Windows 11 SDK and Visual Studio SDK checkboxes are selected to be able to start using Native Image.

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.

But What's the purpose behind installing Visual Studio for our GraalVM Native Image setup?

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:

  • When you create a native image using GraalVM on Windows, it needs a little help from Visual Studio.

1. Compilation Process

  • Native Image: GraalVM's Native Image compiles Java code into a native executable. This process involves translating Java bytecode into machine code that the operating system can run directly.
  • C/C++ Toolchain: On Windows, creating native executables from Java code using Native Image involves generating and linking native machine code. This process requires tools typically used in C/C++ development, such as a compiler and linker.

2. Role of Visual Studio and MSVC

  • Visual Studio: Visual Studio is an integrated development environment (IDE) that includes a suite of development tools. These tools include the Microsoft Visual C++ (MSVC) compiler and linker.
  • MSVC: MSVC is a crucial component provided by Visual Studio. It includes:
    • C/C++ Compiler: Converts source code into machine code.
    • Linker: Combines object files into an executable binary.
  • Compatibility: GraalVM Native Image requires a specific version of MSVC to ensure compatibility and proper functionality. Visual Studio 2022 version 17.6.0 or later provides the necessary MSVC tools.

3. Why Not Just Any C/C++ Compiler?

  • Integration and Support: MSVC is well-integrated with the Windows operating system and provides robust support for Windows-specific features and optimizations.
  • Dependencies: The Native Image toolchain for Windows is designed and tested to work seamlessly with MSVC. Using other compilers might result in compatibility issues or lack of necessary support for certain features.
  • Libraries and Headers: MSVC includes libraries and headers that are essential for building native executables. These are standard in Windows development and ensure that the generated binaries work correctly with Windows APIs.

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: <span class="pink">native-image Main</span>.

💡 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.

File explorer screenshot displaying a list of Java source files (`Main.java`, `SimpleTask.java`, `TaskRunner.java`), class files (`Main.class`, `TaskRunner.class`, `SimpleTask.class`), and a platform-specific executable file (`main.exe`) created using GraalVM Native Image, highlighting the file structure and size for each file.

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 <span class="pink">./main</span> 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:

	

PS D:\PerformanceTest\src> native-image Main
========================================================================================================================
GraalVM Native Image: Generating 'main' (executable)...
========================================================================================================================
For detailed information and explanations on the build output, visit:
https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/BuildOutput.md
------------------------------------------------------------------------------------------------------------------------
[1/8] Initializing...                                                                                    (5.4s @ 0.09GB)
 Java version: 22.0.1+8, vendor version: GraalVM CE 22.0.1+8.1
 Graal compiler: optimization level: 2, target machine: x86-64-v3
 C compiler: cl.exe (microsoft, x64, 19.40.33808)
 Garbage collector: Serial GC (max heap size: 80% of RAM)
 1 user-specific feature(s):
 - com.oracle.svm.thirdparty.gson.GsonFeature
------------------------------------------------------------------------------------------------------------------------
Build resources:
 - 8.86GB of memory (75.6% of 11.73GB system memory, determined at start)
 - 4 thread(s) (100.0% of 4 available processor(s), determined at start)
[2/8] Performing analysis...  [****]                                                                    (45.9s @ 0.26GB)
    3,156 reachable types   (72.0% of    4,381 total)
    3,674 reachable fields  (42.7% of    8,600 total)
   14,733 reachable methods (44.0% of   33,453 total)
    1,015 types,    53 fields, and   531 methods registered for reflection
       61 types,    51 fields, and    52 methods registered for JNI access
        1 native library: version
[3/8] Building universe...                                                                               (7.2s @ 0.30GB)
[4/8] Parsing methods...      [**]                                                                       (4.6s @ 0.30GB)
[5/8] Inlining methods...     [***]                                                                      (3.7s @ 0.38GB)
[6/8] Compiling methods...    [******]                                                                  (43.2s @ 0.28GB)
[7/8] Laying out methods...   [**]                                                                       (3.3s @ 0.35GB)
[8/8] Creating image...       [**]                                                                       (4.4s @ 0.37GB)
   5.08MB (40.19%) for code area:     8,346 compilation units
   7.40MB (58.51%) for image heap:   89,480 objects and 47 resources
 167.59kB ( 1.29%) for other data
  12.64MB in total
------------------------------------------------------------------------------------------------------------------------
Top 10 origins of code area:                                Top 10 object types in image heap:
   3.74MB java.base                                            1.50MB byte[] for code metadata
 970.77kB svm.jar (Native Image)                               1.20MB byte[] for java.lang.String
 114.75kB java.logging                                       903.81kB java.lang.String
  65.47kB org.graalvm.nativeimage.base                       747.46kB java.lang.Class
  45.84kB jdk.proxy3                                         487.51kB heap alignment
  44.42kB jdk.proxy1                                         296.02kB byte[] for general heap data
  26.61kB jdk.internal.vm.ci                                 271.22kB com.oracle.svm.core.hub.DynamicHubCompanion
  22.64kB org.graalvm.collections                            240.56kB java.util.HashMap$Node
  11.42kB jdk.proxy2                                         194.59kB java.lang.Object[]
   8.10kB jdk.graal.compiler                                 179.48kB java.lang.String[]
   5.44kB for 3 more packages                                  1.45MB for 876 more object types
------------------------------------------------------------------------------------------------------------------------
Recommendations:
 HEAP: Set max heap for improved and more predictable memory usage.
 CPU:  Enable more CPU features with '-march=native' for improved performance.
------------------------------------------------------------------------------------------------------------------------
                        8.9s (7.3% of total time) in 191 GCs | Peak RSS: 0.81GB | CPU load: 2.95
------------------------------------------------------------------------------------------------------------------------
Build artifacts:
 D:\PerformanceTest\src\main.exe (executable)
========================================================================================================================
Finished generating 'main' in 1m 59s.
PS D:\PerformanceTest\src> ./main
Result: 30000000
Execution time: 16 ms
PS D:\PerformanceTest\src>

💡 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.

Chapter 1.4: Considerations

When the JDK Might Be a Better Choice Than a Native Image:

  • High Traffic Websites: For websites with a lot of visitors, the JDK's Just-In-Time (JIT) compilation can optimize performance over time, making it more efficient for handling large amounts of traffic.
  • Heavy Memory and CPU Usage: Applications that need a lot of memory and CPU power can benefit from the JDK's advanced garbage collection and performance tuning capabilities.
  • Frequent Deployments: If you're updating and deploying your application often, the JDK allows for faster turnaround times since you don't need to recompile the entire application into a native image each time.
  • Large Monolithic Applications: Big, complex applications that aren't broken down into microservices may perform better with the JDK because it can optimize and manage the resources needed for such extensive codebases more effectively.

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.

Chapter 2: Building a Native Executable from a JAR File

Introduction

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.

Why GraalVM with Spring Boot?

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 <span class="pink">java -jar</span> command and with a native executable.

To begin, go to start.spring.io and generate a Maven Spring Boot project with the following dependencies:

  • GraalVM Native Support→The "GraalVM Native Support" dependency enables the compilation of Java applications into native executables using GraalVM, offering improved performance and reduced startup times.
  • Spring Web
Screenshot of the Spring Initializr interface, with Maven selected as the build tool, Java as the language, and Spring Boot version 3.3.0. Dependencies chosen include GraalVM Native Support and Spring Web, and the project group is set to "com.unlogged."

Our code should look something like this:

HomeController.java

	
package com.unlogged;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HomeController {
    @GetMapping("/hello")
    public String Home(){
        return  "Hello Native Image from SpringBoot Jar";
    }
}

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 <span class="pink">mvn clean package</span>. 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:

	
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

Install the latest PowerShell for new features and improvements! https://aka.ms/PSWindows

PS D:\GraalVMSpringDemo> mvn clean package
[INFO] Scanning for projects...
[INFO] 
[INFO] -------------------< com.unlogged:GraalVMSpringDemo >-------------------
[INFO] Building GraalVMSpringDemo 0.0.1-SNAPSHOT
[INFO]   from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- clean:3.3.2:clean (default-clean) @ GraalVMSpringDemo ---
[INFO] 
[INFO] --- resources:3.3.1:resources (default-resources) @ GraalVMSpringDemo ---
[INFO] Copying 1 resource from src\main\resources to target\classes
[INFO] Copying 0 resource from src\main\resources to target\classes
[INFO]
[INFO] --- compiler:3.13.0:compile (default-compile) @ GraalVMSpringDemo ---
[INFO] Recompiling the module because of changed source code.
[INFO] Compiling 2 source files with javac [debug parameters release 22] to target\classes
[INFO] 
[INFO] --- resources:3.3.1:testResources (default-testResources) @ GraalVMSpringDemo ---
[INFO] skip non existing resourceDirectory D:\GraalVMSpringDemo\src\test\resources
[INFO]
[INFO] --- compiler:3.13.0:testCompile (default-testCompile) @ GraalVMSpringDemo ---
[INFO] Recompiling the module because of changed dependency.
[INFO] Compiling 1 source file with javac [debug parameters release 22] to target\test-classes
[INFO] 
[INFO] --- surefire:3.2.5:test (default-test) @ GraalVMSpringDemo ---
[WARNING]  Parameter 'systemProperties' is deprecated: Use systemPropertyVariables instead.
[INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider
Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/surefire/surefire-junit-platform/3.2.5/surefire-junit-platform-3.2.5.pom
Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/maven/surefire/surefire-junit-platform/3.2.5/surefire-junit-platform-3.2.5.pom (4.7 kB at 5.3 kB/s)
Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/surefire/surefire-providers/3.2.5/surefire-providers-3.2.5.pom
Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/maven/surefire/surefire-providers/3.2.5/surefire-providers-3.2.5.pom (2.6 kB at 31 kB/s)
Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/surefire/common-java5/3.2.5/common-java5-3.2.5.pom
Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/maven/surefire/common-java5/3.2.5/common-java5-3.2.5.pom (2.8 kB at 46 kB/s)
Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/surefire/surefire-junit-platform/3.2.5/surefire-junit-platform-3.2.5.jar
Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/maven/surefire/surefire-junit-platform/3.2.5/surefire-junit-platform-3.2.5.jar (27 kB at 274 kB/s)
Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/surefire/common-java5/3.2.5/common-java5-3.2.5.jar
Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/maven/surefire/common-java5/3.2.5/common-java5-3.2.5.jar (18 kB at 167 kB/s)
[INFO] 
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.unlogged.GraalVmSpringDemoApplicationTests
01:16:31.108 [main] INFO org.springframework.test.context.support.AnnotationConfigContextLoaderUtils -- Cou
ld not detect default configuration classes for test class [com.unlogged.GraalVmSpringDemoApplicationTests]
: GraalVmSpringDemoApplicationTests does not declare any static, non-private, non-final, nested classes annotated with @Configuration.
01:16:31.281 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper -- Found @
SpringBootConfiguration com.unlogged.GraalVmSpringDemoApplication for test class com.unlogged.GraalVmSpringDemoApplicationTests
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/

 :: Spring Boot ::                (v3.3.0)

2024-05-30T01:16:31.879+05:30  INFO 4288 --- [GraalVMSpringDemo] [           main] c.u.GraalVmSpringDemoApp
licationTests    : Starting GraalVmSpringDemoApplicationTests using Java 22.0.1 with PID 4288 (started by Gaurav in D:\GraalVMSpringDemo)
2024-05-30T01:16:31.884+05:30  INFO 4288 --- [GraalVMSpringDemo] [           main] c.u.GraalVmSpringDemoApplicationTests    : No active profile set, falling back to 1 default profile: "default"
2024-05-30T01:16:33.335+05:30  INFO 4288 --- [GraalVMSpringDemo] [           main] c.u.GraalVmSpringDemoApplicationTests    : Started GraalVmSpringDemoApplicationTests in 1.898 seconds (process running for 3.994)  
WARNING: A Java agent has been loaded dynamically (C:\Users\Gaurav\.m2\repository\net\bytebuddy\byte-buddy-agent\1.14.16\byte-buddy-agent-1.14.16.jar)
WARNING: If a serviceability tool is in use, please run with -XX:+EnableDynamicAgentLoading to hide this warning
WARNING: If a serviceability tool is not in use, please run with -Djdk.instrument.traceUsage for more information
WARNING: Dynamic loading of agents will be disallowed by default in a future release
OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 3.335 s -- in com.unlogged.GraalVmSpringDemoApplicationTests
[INFO] 
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO]
[INFO] --- jar:3.4.1:jar (default-jar) @ GraalVMSpringDemo ---
[INFO] Building jar: D:\GraalVMSpringDemo\target\GraalVMSpringDemo-0.0.1-SNAPSHOT.jar
[INFO] 
[INFO] --- spring-boot:3.3.0:repackage (repackage) @ GraalVMSpringDemo ---
[INFO] Replacing main artifact D:\GraalVMSpringDemo\target\GraalVMSpringDemo-0.0.1-SNAPSHOT.jar with repackaged archive, adding nested dependencies in BOOT-INF/.
[INFO] The original artifact has been renamed to D:\GraalVMSpringDemo\target\GraalVMSpringDemo-0.0.1-SNAPSHOT.jar.original
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  12.053 s
[INFO] Finished at: 2024-05-30T01:16:35+05:30
[INFO] ------------------------------------------------------------------------

Building our native executable:

This command <span class="pink">mvn -Pnative native:compile -DskipTests</span> instructs Maven to:

  • Build a native executable using the GraalVM compiler (<span class="pink">native:compile</span>) from the Spring Boot project.
  • The <span class="pink">Pnative</span> flag activates the Maven profile configured for native compilation.
  • The <span class="pink">DskipTests</span> flag skips running any tests during the compilation process.

Windows PowerShell:

	


PS D:\GraalVMSpringDemo> mvn -Pnative native:compile -DskipTests
[INFO] Scanning for projects...
[INFO] 
[INFO] -------------------< com.unlogged:GraalVMSpringDemo >-------------------
[INFO] Building GraalVMSpringDemo 0.0.1-SNAPSHOT
[INFO]   from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] >>> native:0.10.2:compile (default-cli) > package @ GraalVMSpringDemo >>>
[INFO] 
[INFO] --- native:0.10.2:add-reachability-metadata (add-reachability-metadata) @ GraalVMSpringDemo ---     
Downloading from central: https://repo.maven.apache.org/maven2/org/graalvm/buildtools/graalvm-reachability-metadata/0.10.2/graalvm-reachability-metadata-0.10.2-repository.zip
Downloaded from central: https://repo.maven.apache.org/maven2/org/graalvm/buildtools/graalvm-reachability-metadata/0.10.2/graalvm-reachability-metadata-0.10.2-repository.zip (251 kB at 281 kB/s)
[INFO] Downloaded GraalVM reachability metadata repository from file:/C:/Users/Gaurav/.m2/repository/org/graalvm/buildtools/graalvm-reachability-metadata/0.10.2/graalvm-reachability-metadata-0.10.2-repository.zip  
[INFO] [graalvm reachability metadata repository for ch.qos.logback:logback-classic:1.5.6]: Configuration directory not found. Trying latest version.
[INFO] [graalvm reachability metadata repository for ch.qos.logback:logback-classic:1.5.6]: Configuration directory is ch.qos.logback\logback-classic\1.4.9
[INFO] [graalvm reachability metadata repository for com.fasterxml.jackson.core:jackson-databind:2.17.1]: Configuration directory not found. Trying latest version.
[INFO] [graalvm reachability metadata repository for com.fasterxml.jackson.core:jackson-databind:2.17.1]: Configuration directory is com.fasterxml.jackson.core\jackson-databind\2.15.2
[INFO] [graalvm reachability metadata repository for org.apache.tomcat.embed:tomcat-embed-core:10.1.24]: Configuration directory not found. Trying latest version.
[INFO] [graalvm reachability metadata repository for org.apache.tomcat.embed:tomcat-embed-core:10.1.24]: Configuration directory is org.apache.tomcat.embed\tomcat-embed-core\10.0.20
[INFO] 
[INFO] --- resources:3.3.1:resources (default-resources) @ GraalVMSpringDemo ---
[INFO] Copying 1 resource from src\main\resources to target\classes
[INFO] Copying 0 resource from src\main\resources to target\classes
[INFO]
[INFO] --- compiler:3.13.0:compile (default-compile) @ GraalVMSpringDemo ---
[INFO] Nothing to compile - all classes are up to date.
[INFO]
[INFO] --- resources:3.3.1:testResources (default-testResources) @ GraalVMSpringDemo ---
[INFO] skip non existing resourceDirectory D:\GraalVMSpringDemo\src\test\resources
[INFO]
[INFO] --- compiler:3.13.0:testCompile (default-testCompile) @ GraalVMSpringDemo ---
[INFO] Nothing to compile - all classes are up to date.
[INFO]
[INFO] --- surefire:3.2.5:test (default-test) @ GraalVMSpringDemo ---
[WARNING]  Parameter 'systemProperties' is deprecated: Use systemPropertyVariables instead.
[INFO] Tests are skipped.
[INFO]
[INFO] --- spring-boot:3.3.0:process-aot (process-aot) @ GraalVMSpringDemo ---
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/

 :: Spring Boot ::                (v3.3.0)

2024-05-30T01:23:06.204+05:30  INFO 13372 --- [GraalVMSpringDemo] [           main] c.unlogged.GraalVmSprin
gDemoApplication  : Starting GraalVmSpringDemoApplication using Java 22.0.1 with PID 13372 (D:\GraalVMSpringDemo\target\classes started by Gaurav in D:\GraalVMSpringDemo)
2024-05-30T01:23:06.208+05:30  INFO 13372 --- [GraalVMSpringDemo] [           main] c.unlogged.GraalVmSpringDemoApplication  : No active profile set, falling back to 1 default profile: "default"
[INFO] 
[INFO] --- jar:3.4.1:jar (default-jar) @ GraalVMSpringDemo ---
[INFO] Building jar: D:\GraalVMSpringDemo\target\GraalVMSpringDemo-0.0.1-SNAPSHOT.jar
[INFO] 
[INFO] --- spring-boot:3.3.0:repackage (repackage) @ GraalVMSpringDemo ---
[INFO] Replacing main artifact D:\GraalVMSpringDemo\target\GraalVMSpringDemo-0.0.1-SNAPSHOT.jar with repackaged archive, adding nested dependencies in BOOT-INF/.
[INFO] The original artifact has been renamed to D:\GraalVMSpringDemo\target\GraalVMSpringDemo-0.0.1-SNAPSHOT.jar.original
[INFO]
[INFO] <<< native:0.10.2:compile (default-cli) < package @ GraalVMSpringDemo <<<
[INFO]
[INFO] 
[INFO] --- native:0.10.2:compile (default-cli) @ GraalVMSpringDemo ---
[INFO] Found GraalVM installation from GRAALVM_HOME variable.
[INFO] [graalvm reachability metadata repository for ch.qos.logback:logback-classic:1.5.6]: Configuration directory not found. Trying latest version.
[INFO] [graalvm reachability metadata repository for ch.qos.logback:logback-classic:1.5.6]: Configuration directory is ch.qos.logback\logback-classic\1.4.9
[INFO] [graalvm reachability metadata repository for com.fasterxml.jackson.core:jackson-databind:2.17.1]: Configuration directory not found. Trying latest version.
[INFO] [graalvm reachability metadata repository for com.fasterxml.jackson.core:jackson-databind:2.17.1]: Configuration directory is com.fasterxml.jackson.core\jackson-databind\2.15.2
[INFO] [graalvm reachability metadata repository for org.apache.tomcat.embed:tomcat-embed-core:10.1.24]: Configuration directory not found. Trying latest version.
[INFO] [graalvm reachability metadata repository for org.apache.tomcat.embed:tomcat-embed-core:10.1.24]: Configuration directory is org.apache.tomcat.embed\tomcat-embed-core\10.0.20
[INFO] Executing: C:\Program Files\Java\graalvm-community-openjdk-22.0.1+8.1\bin\native-image.cmd @target\tmp\native-image-4785695430542719095.args
Warning: The option '-H:ResourceConfigurationResources=META-INF/native-image/org.apache.tomcat.embed/tomcat
-embed-core/tomcat-resource.json' is experimental and must be enabled via '-H:+UnlockExperimentalVMOptions' in the future.
Warning: The option '-H:ReflectionConfigurationResources=META-INF/native-image/org.apache.tomcat.embed/tomc
at-embed-core/tomcat-reflection.json' is experimental and must be enabled via '-H:+UnlockExperimentalVMOptions' in the future.
Warning: The option '-H:ReflectionConfigurationResources=META-INF/native-image/org.apache.tomcat.embed/tomc
at-embed-websocket/tomcat-reflection.json' is experimental and must be enabled via '-H:+UnlockExperimentalVMOptions' in the future.
Warning: The option '-H:ReflectionConfigurationResources=META-INF/native-image/org.apache.tomcat.embed/tomc
at-embed-el/tomcat-reflection.json' is experimental and must be enabled via '-H:+UnlockExperimentalVMOptions' in the future.
Warning: The option '-H:ResourceConfigurationResources=META-INF/native-image/org.apache.tomcat.embed/tomcat
-embed-el/tomcat-resource.json' is experimental and must be enabled via '-H:+UnlockExperimentalVMOptions' in the future.
Warning: The option '-H:ResourceConfigurationResources=META-INF/native-image/org.apache.tomcat.embed/tomcat
-embed-websocket/tomcat-resource.json' is experimental and must be enabled via '-H:+UnlockExperimentalVMOptions' in the future.
Warning: Please re-evaluate whether any experimental option is required, and either remove or unlock it. Th
e build output lists all active experimental options, including where they come from and possible alternatives. If you think an experimental option should be considered as stable, please file an issue.
===========================================================================================================
GraalVM Native Image: Generating 'GraalVMSpringDemo' (executable)...
===========================================================================================================
For detailed information and explanations on the build output, visit:
https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/BuildOutput.md
-----------------------------------------------------------------------------------------------------------
[1/8] Initializing...                                                                       (9.1s @ 0.11GB)
 Java version: 22.0.1+8, vendor version: GraalVM CE 22.0.1+8.1
 Graal compiler: optimization level: 2, target machine: x86-64-v3
 C compiler: cl.exe (microsoft, x64, 19.40.33808)
 Garbage collector: Serial GC (max heap size: 80% of RAM)
 2 user-specific feature(s):
 - com.oracle.svm.thirdparty.gson.GsonFeature
 - org.springframework.aot.nativex.feature.PreComputeFieldFeature
-----------------------------------------------------------------------------------------------------------
 2 experimental option(s) unlocked:
 - '-H:ResourceConfigurationResources': Use a resource-config.json in your META-INF/native-image/<groupID>/
<artifactID> directory instead. (origin(s): 'META-INF\native-image\org.apache.tomcat.embed\tomcat-embed-cor
e\native-image.properties' in 'file:///C:/Users/Gaurav/.m2/repository/org/apache/tomcat/embed/tomcat-embed-
core/10.1.24/tomcat-embed-core-10.1.24.jar', 'META-INF\native-image\org.apache.tomcat.embed\tomcat-embed-el
\native-image.properties' in 'file:///C:/Users/Gaurav/.m2/repository/org/apache/tomcat/embed/tomcat-embed-e
l/10.1.24/tomcat-embed-el-10.1.24.jar', 'META-INF\native-image\org.apache.tomcat.embed\tomcat-embed-websock
et\native-image.properties' in 'file:///C:/Users/Gaurav/.m2/repository/org/apache/tomcat/embed/tomcat-embed-websocket/10.1.24/tomcat-embed-websocket-10.1.24.jar')
 - '-H:ReflectionConfigurationResources': Use a reflect-config.json in your META-INF/native-image/<groupID>
/<artifactID> directory instead. (origin(s): 'META-INF\native-image\org.apache.tomcat.embed\tomcat-embed-co
re\native-image.properties' in 'file:///C:/Users/Gaurav/.m2/repository/org/apache/tomcat/embed/tomcat-embed
-core/10.1.24/tomcat-embed-core-10.1.24.jar', 'META-INF\native-image\org.apache.tomcat.embed\tomcat-embed-e
l\native-image.properties' in 'file:///C:/Users/Gaurav/.m2/repository/org/apache/tomcat/embed/tomcat-embed-
el/10.1.24/tomcat-embed-el-10.1.24.jar', 'META-INF\native-image\org.apache.tomcat.embed\tomcat-embed-websoc
ket\native-image.properties' in 'file:///C:/Users/Gaurav/.m2/repository/org/apache/tomcat/embed/tomcat-embed-websocket/10.1.24/tomcat-embed-websocket-10.1.24.jar')
-----------------------------------------------------------------------------------------------------------
Build resources:
 - 8.86GB of memory (75.6% of 11.73GB system memory, determined at start)
 - 4 thread(s) (100.0% of 4 available processor(s), determined at start)
SLF4J(W): No SLF4J providers were found.
SLF4J(W): Defaulting to no-operation (NOP) logger implementation
SLF4J(W): See https://www.slf4j.org/codes.html#noProviders for further details.
[2/8] Performing analysis...  [*****]                                                     (113.0s @ 1.83GB)
   16,751 reachable types   (89.5% of   18,724 total)
   25,946 reachable fields  (59.7% of   43,479 total)
   81,354 reachable methods (63.3% of  128,567 total)
    5,869 types,   415 fields, and 6,251 methods registered for reflection
       78 types,    62 fields, and    68 methods registered for JNI access
        5 native libraries: crypt32, ncrypt, psapi, version, winhttp
[3/8] Building universe...                                                                 (19.7s @ 1.49GB)
[4/8] Parsing methods...      [***]                                                         (8.6s @ 1.67GB)
[5/8] Inlining methods...     [****]                                                        (8.3s @ 1.56GB)
[6/8] Compiling methods...    [*********]                                                  (89.3s @ 1.81GB)
[7/8] Laying out methods...   [***]                                                         (9.1s @ 2.81GB)
[8/8] Creating image...       [***]                                                         (8.8s @ 2.34GB)
  39.70MB (51.38%) for code area:    52,268 compilation units
  37.11MB (48.02%) for image heap:  386,406 objects and 365 resources
 474.52kB ( 0.60%) for other data
  77.27MB in total
-----------------------------------------------------------------------------------------------------------
Top 10 origins of code area:                         Top 10 object types in image heap:
  14.66MB java.base                                    11.88MB byte[] for code metadata
   4.48MB tomcat-embed-core-10.1.24.jar                 5.58MB byte[] for java.lang.String
   3.76MB java.xml                                      4.09MB java.lang.Class
   2.06MB jackson-databind-2.17.1.jar                   3.72MB java.lang.String
   1.58MB svm.jar (Native Image)                        1.41MB com.oracle.svm.core.hub.DynamicHubCompanion 
   1.57MB spring-core-6.1.8.jar                         1.04MB byte[] for reflection metadata
   1.42MB spring-boot-3.3.0.jar                       887.82kB byte[] for embedded resources
 948.91kB spring-beans-6.1.8.jar                      861.66kB byte[] for general heap data
 911.81kB spring-web-6.1.8.jar                        752.09kB java.lang.String[]
 879.36kB spring-webmvc-6.1.8.jar                     601.05kB c.o.s.c.hub.DynamicHub$ReflectionMetadata
   7.11MB for 69 more packages                          6.36MB for 3396 more object types
-----------------------------------------------------------------------------------------------------------
Recommendations:
 HEAP: Set max heap for improved and more predictable memory usage.
 CPU:  Enable more CPU features with '-march=native' for improved performance.
-----------------------------------------------------------------------------------------------------------
                38.4s (14.1% of total time) in 220 GCs | Peak RSS: 4.05GB | CPU load: 2.67
-----------------------------------------------------------------------------------------------------------
Build artifacts:
 D:\GraalVMSpringDemo\target\GraalVMSpringDemo.exe (executable)
===========================================================================================================
Finished generating 'GraalVMSpringDemo' in 4m 30s.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  04:46 min
[INFO] Finished at: 2024-05-30T01:27:47+05:30
[INFO] ------------------------------------------------------------------------


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.

IDE view showing a project structure on the left, with files and folders like graalvm-reachability-metadata, spring-aot, and GraalVMSpringDemo.exe. The right side shows the HomeController.java file, with a HomeController class annotated as a RestController and a GetMapping for the /hello endpoint, returning "Hello Native Image from Spring."

Now let’s see,  how fast our Spring Boot application starts up by running the jar file with the usual <span class="pink">java -jar fileName</span> command.

Windows PowerShell:

	
PS D:\GraalVMSpringDemo> java -jar .\target\GraalVMSpringDemo-0.0.1-SNAPSHOT.jar
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/

 :: Spring Boot ::                (v3.3.0)

2024-05-30T01:44:52.985+05:30  INFO 8152 --- [GraalVMSpringDemo] [           main] c.unlogged.GraalVmSpring
DemoApplication  : Starting GraalVmSpringDemoApplication v0.0.1-SNAPSHOT using Java 22.0.1 with PID 8152 (D:\GraalVMSpringDemo\target\GraalVMSpringDemo-0.0.1-SNAPSHOT.jar started by Gaurav in D:\GraalVMSpringDemo) 
2024-05-30T01:44:52.989+05:30  INFO 8152 --- [GraalVMSpringDemo] [           main] c.unlogged.GraalVmSpringDemoApplication  : No active profile set, falling back to 1 default profile: "default"
2024-05-30T01:44:54.479+05:30  INFO 8152 --- [GraalVMSpringDemo] [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port 8080 (http)
2024-05-30T01:44:54.519+05:30  INFO 8152 --- [GraalVMSpringDemo] [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2024-05-30T01:44:54.519+05:30  INFO 8152 --- [GraalVMSpringDemo] [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.24]
2024-05-30T01:44:54.583+05:30  INFO 8152 --- [GraalVMSpringDemo] [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2024-05-30T01:44:54.584+05:30  INFO 8152 --- [GraalVMSpringDemo] [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1489 ms
2024-05-30T01:44:55.021+05:30  INFO 8152 --- [GraalVMSpringDemo] [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port 8080 (http) with context path '/'
2024-05-30T01:44:55.043+05:30  INFO 8152 --- [GraalVMSpringDemo] [           main] c.unlogged.GraalVmSpringDemoApplication  : Started GraalVmSpringDemoApplication in 2.747 seconds (process running for 3.391)       
2024-05-30T01:45:19.650+05:30  INFO 8152 --- [GraalVMSpringDemo] [nio-8080-exec-2] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2024-05-30T01:45:19.650+05:30  INFO 8152 --- [GraalVMSpringDemo] [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2024-05-30T01:45:19.652+05:30  INFO 8152 --- [GraalVMSpringDemo] [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : Completed initialization in 2 ms

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.

	
PS D:\GraalVMSpringDemo> ./target/GraalVMSpringDemo
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/

 :: Spring Boot ::                (v3.3.0)

2024-05-30T01:47:44.203+05:30  INFO 4220 --- [GraalVMSpringDemo] [           main] c.unlogged.GraalVmSpring
DemoApplication  : Starting AOT-processed GraalVmSpringDemoApplication using Java 22.0.1 with PID 4220 (D:\GraalVMSpringDemo\target\GraalVMSpringDemo.exe started by Gaurav in D:\GraalVMSpringDemo)
2024-05-30T01:47:44.204+05:30  INFO 4220 --- [GraalVMSpringDemo] [           main] c.unlogged.GraalVmSpringDemoApplication  : No active profile set, falling back to 1 default profile: "default"
2024-05-30T01:47:44.230+05:30  INFO 4220 --- [GraalVMSpringDemo] [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port 8080 (http)
2024-05-30T01:47:44.232+05:30  INFO 4220 --- [GraalVMSpringDemo] [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2024-05-30T01:47:44.232+05:30  INFO 4220 --- [GraalVMSpringDemo] [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.24]
2024-05-30T01:47:44.241+05:30  INFO 4220 --- [GraalVMSpringDemo] [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2024-05-30T01:47:44.241+05:30  INFO 4220 --- [GraalVMSpringDemo] [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 37 ms
2024-05-30T01:47:44.265+05:30  INFO 4220 --- [GraalVMSpringDemo] [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port 8080 (http) with context path '/'
2024-05-30T01:47:44.267+05:30  INFO 4220 --- [GraalVMSpringDemo] [           main] c.unlogged.GraalVmSpringDemoApplication  : Started GraalVmSpringDemoApplication in 0.078 seconds (process running for 0.086) 

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 <span class="pink">java -jar</span> 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.

Chapter 3: Some Additional Stuff

We are not going into detail of these.Feel free to explore if you find them intriguing.

Additional Feature that comes with using GraalVM:

Polyglot Capabilities:

  • GraalVM supports running code written in other programming languages along with Java. This means you can write parts of your application in languages like JavaScript, Python, Ruby, or R, and run them together seamlessly. This can be very handy if you want to use different languages for different tasks within the same project.

Additional Ongoing Graal Projects:

A list of ongoing Graal projects, presented as labeled icons, includes Graal Stack (GS), Graal Cloud Native (GCN), and Graal OS (GO) on the left side, with GraalJS, GraalPy, Java on Truffle, and Ruby on the right side. Each label is paired with an icon representing its respective technology.

💡 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...

Gaurav Sharma
May 30, 2024
Use Unlogged to
mock instantly
record and replay methods
mock instantly
Install Plugin