Native Image
AoT Compiler (Ahead of Time Compiler) #
Unlike traditional JIT compilers, an AoT compiler creates an executable file for a Java program, similar to C language, and the application can be run simply by executing that file.

- JIT: Compiles and optimizes during runtime using a compiler
- AoT: Executes a native executable pre-compiled by a compiler
AoT stands for Ahead of Time Compiler, which means compiling code into machine language tailored for a specific device in advance.
Optimization also occurs during the compilation process. After compilation, an immediately executable native file is created.
Unlike JIT, an AoT compiler does not perform optimization once the executable file is launched.
JIT translates bytecode into machine code at application runtime and optimizes it based on execution environment information obtained through profiling during that process. For example, if it's an Intel CPU, the bytecode is translated and optimized to suit the Intel CPU. For this reason, JIT compilers are also called dynamic compilers.
Conversely, AoT compilers compile into machine code already tailored for a specific environment, making it difficult to collect runtime environment information. Additionally, they perform heavy and complex analysis and optimization during compilation. For this reason, AoT compilers are called static compilers.
Native Image #
Native Image is a technology that compiles Java code into a binary, i.e., ahead of time.
Native executables only include the code required at runtime (application classes, library classes, language runtime, and statically linked native JDK code).
GraalVM has newly added an AoT compiler and started supporting Native Image.
In other words, the era has arrived where Java code can be executed without a JVM.

Executable files generated as native images offer the following advantages:
- Uses only a fraction of the resources required by a JVM, resulting in lower execution costs
- Starts in milliseconds.
- Provides peak performance instantly, without warming up.
- Can be packaged into lightweight container images for fast and efficient deployment.
- And many, many more!
Native executables are generated by the Native Image builder or Native Image tool.
It processes application classes and other metadata to produce a binary for a specific operating system and architecture.
The Native Image tool processes tasks in the following order, and this process is called build time to clearly distinguish it from compiling Java code into bytecode.
- Performs static analysis to determine reachable classes and methods when the application runs.
- Compiles classes, methods, and resources into a binary.
With GraalVM, you can compile Java bytecode into platform-specific, self-contained native executables, allowing applications to start faster and reduce their footprint.
The Native Image feature is not provided by default, so it can be easily installed via the GraalVM updater. Note that GraalVM primarily supports Linux and Mac.
Native Image installation command
gu install native-image
Performance and Limitations #
While the above descriptions make Native Image seem excellent and performant, actual testing can sometimes reveal unexpected situations.

Looking at the graph above, the AoT compiler shows significantly superior performance initially.
This is because the AoT compiler has already translated and optimized the code into machine language tailored for a specific environment.
However, over time, the JIT compiler demonstrates higher performance.
This is because the JIT compiler performs optimization based on runtime environment information after execution.
What we can learn here is that AoT is not always faster than JIT. Native Image was not developed because JIT was slow.
Native Image is not inherently fast, but rather its initial startup is extremely quick. It was developed with a focus on rapid initial execution and processing. Therefore, overall optimization techniques are still needed.
Furthermore, native builds take a long time because they require compilation and optimization tailored to a specific environment, which is another issue that needs to be addressed.
Why is Native Image startup fast? #
It's because of heap images.
This means that by creating images of static blocks or class loading, for example, faster execution becomes possible.
However, this also presents problems, specifically concerning dynamic technologies such as reflection, class loading, and dynamic proxies.
Since the AoT compiler operates statically, there are many issues related to dynamic technologies.
Frameworks like Spring heavily utilize dynamic technologies, and many related issues are being resolved through collaboration with GraalVM.

The image above compares AoT and JIT.
AoT offers advantages such as fast execution, small footprint, and low memory usage. This makes it suitable for serverless architectures. While the situation might change with the introduction of more optimization techniques, it is currently considered most suitable for serverless environments.
Ultimately, both are necessary, and their suitability depends on the situation. AoT would be more appropriate for scenarios requiring fast execution, like serverless, while JIT would be better for server environments where applications run for extended periods.
But then? #
However, these aspects begin to show a different pattern starting with GraalVM that supports Java 21!
According to the GraalVM for JDK 21 post, applying profile-guided optimization now consistently shows superior performance compared to JIT.
Although this content is based on OracleVM's Native Image, the important point is that it can now surpass JRE JIT.

Next time, we will explore Native Image's memory, optimization techniques, and GCs.