ahead of time class loading & linking - feature imageahead of time class loading & linking - feature image
HappyCoders Glasses

Ahead-of-Time Class Loading & Linking Turbo for Java Applications

Sven Woltmann
Sven Woltmann
Last update: June 12, 2025

In this article, you will learn:

  • Why do large Java applications take several seconds to start?
  • What is Ahead-of-Time Class Loading & Linking, and how can it improve startup time?
  • Step by step: How can Ahead-of-Time Class Loading & Linking accelerate the start of an application?
  • How does AoT Class Loading & Linking differ from (App)CDS?

Why Do Java Applications Start So Slowly?

Java applications are highly flexible at runtime, allowing classes to be dynamically loaded and unloaded. The dynamic compilation, optimization, and de-optimization make Java programs as fast as C code (or faster), and reflection makes frameworks like Jakarta EE, Spring Boot, Quarkus, Helidon, Micronaut, etc., possible in the first place.

But these advantages come at a price:

When starting an application, hundreds of .jar files must be unpacked, and thousands of .class files must be loaded into memory, analyzed, and linked. The static initialization code of classes must be executed, and frameworks like Jakarta EE and Spring must scan the code for annotations, instantiate beans, and execute configuration code.

Large backend applications can thus take several seconds or even minutes to start.

How Can Application Startup Be Accelerated?

Many of the initialization tasks described in the previous section are the same for each application start.

As part of Project Leyden, work is done to perform as many of these repetitive tasks as possible before starting an application.

Through JDK Enhancement Proposal 483, the first fruits of this work will be released in Java 24: Classes can now (after reading, parsing, loading, and linking) be cached in a binary file – the AOT cache, making them available much faster in a loaded and linked state for future starts of the same application. The JDK developers have measured startup time reductions of up to 42%.

In Java 25, JDK Enhancement Proposal 514, Ahead-of-Time Command-Line Ergonomics, simplified the generation of the AOT cache: instead of two steps, only one needs to be executed.

Also in Java 25, JDK Enhancement Proposal 515, Ahead-of-Time Method Profiling, extended the AOT cache to include runtime statistics on method calls so that the JVM can start compiling hotspots immediately after startup. As a result, a further 19% reduction in start time was measured.

How Does Ahead-Of-Time Class Loading Linking Work?

To accelerate program startup through AoT Class Loading & Linking, we need to perform three steps:

  1. In the first step, the application is started in a so-called training run. During this, the JVM analyzes all loaded and linked classes and generates a configuration file with the relevant information about these classes – and from Java 25 onwards also about method call statistics.
  2. In the second step, the binary cache file is created using this configuration file.
  3. For each subsequent application start, you specify this cache file, and the application loads the classes in loaded and linked form directly from this cache – and from Java 25 onwards, it starts directly with the optimization of the most frequently called methods (“hotspots”).

This procedure sounds more complicated than it is. In the following, I will guide you step by step through these steps using an example application up to the accelerated start of the application.

Step-By-Step Instructions to Follow Along

In this section, I will show you how to use Ahead-of-Time Class Loading Linking using a small application.

Well use a simple demo program that just displays the current time.

Here, we use a compact source file with an instance main method. This way, we don’t have to define a class and can simply write void main() instead of public static void main(String args[]).

Download an Early-Access-Build of Java 24 (Ahead-of-Time Class Loading Linking is only available from Java 24 onwards).

Download Java 24 or an early-access build of Java 25 (Ahead-of-Time Class Loading & Linking is available from Java 24; the -XX:AOTCacheOutput option, which allows us to combine the first two steps into one, is available from Java 25).

Save the following source code in the file AotTest.java:

void main() {
  var now = LocalDateTime.now();
  var nowString = now.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM));
  System.out.println("Hello, it's " + nowString);
}Code language: Java (java)

Compile the code (in Java 24 you must include the --enable-preview --source 24 options, as the Compact Source Files and Instance Main Methods feature is still in the preview stage in Java 24):

javac --enable-preview --source 24 AotTest.javaCode language: plaintext (plaintext)

Create a JAR file:

jar cvf AotTest.jar AotTest.classCode language: plaintext (plaintext)

Then start the training run with the following call:

java -XX:AOTMode=record -XX:AOTConfiguration=AotTest.conf \
    --enable-preview -cp AotTest.jar AotTestCode language: plaintext (plaintext)

This creates the configuration file AotTest.conf. You can open this file with a text editor – it contains a long list of classes and data about these classes.

Then, use the following command to create the class cache in the file AotTest.aot (this command does not execute the application again):

java -XX:AOTMode=create -XX:AOTConfiguration=AotTest.conf -XX:AOTCache=AotTest.aot \
    --enable-preview -cp AotTest.jarCode language: plaintext (plaintext)

In Java 25, you can perform the training run and cache generation (the last two steps before this info block) in one combined step. In addition, the --enable-preview option is no longer required, as Compact Source Files and Instance Main Methods have been finalized in Java 25:

java -XX:AOTCacheOutput=AotTest.aot -cp AotTest.jar AotTest

Finally, start the application specifying the cache file to use:

java -XX:AOTCache=AotTest.aot --enable-preview -cp AotTest.jar AotTestCode language: plaintext (plaintext)

Now, let’s compare the application’s startup time with and without cache.

In the following, I use the Linux command time. On Windows, you can use the tool ptime, which you can install via the package manager Chocolatey using the command choco install ptime.

First, a run without cache:

time java --enable-preview -cp AotTest.jar AotTestCode language: plaintext (plaintext)

With five runs, I got a median runtime of 0.137 seconds.

And now a run with cache:

time java -XX:AOTCache=AotTest.aot --enable-preview -cp AotTest.jar AotTestCode language: plaintext (plaintext)

This time, I observed a median runtime of 0.086 seconds from five runs. That is an impressive performance increase of 37%, which comes close to the 42% measured by the feature’s developers.

And What About AppCDS?

The attentive reader might wonder: What’s the difference between Ahead-of-Time Class Loading & Linking and (Application) Class Data Sharing?

Class Data Sharing (CDS) has existed since Java 5 and allows storing the JDK classes in a platform-specific binary format, from which the classes can then be loaded much faster than from .class files.

In Java 10, Application Class Data Sharing (AppCDS) was added, which allows not only JDK classes but also application classes to be stored in this binary format.

Ahead-of-Time Class Loading & Linking builds upon (App)CDS. If you looked at the file AotTest.conf earlier, you might have noticed that the header says it’s a “CDS archive dump.”

While Class Data Sharing merely reads and parses the classes and then stores them in a binary format, with AoT Class Loading & Linking, the classes are additionally – as the name suggests – loaded into Class objects and linked.

The Leyden developers have tested both mechanisms with the Spring PetClinic. AppCDS accelerated the application’s loading time by 33%, and AoT Class Loading & Linking by 42%. So, if you’re already using AppCDS, the startup time improvement through AoT Class Loading & Linking won’t be quite as significant.

Conclusion

Java is a very flexible and powerful language, but this flexibility can lead to startup times ranging from several seconds to minutes for larger applications. During startup, Java classes are read, parsed, loaded, and linked, among other things.

With Ahead-of-Time Class Loading & Linking, we can perform these steps once before the application starts, thereby – according to the developers of this feature – accelerating the actual start of the application by up to 42%.

Ahead-of-Time Method Profiling also saves statistics on method calls in the AOT cache so that frequently called methods (“hotspots”) can be optimized immediately after the application is started.

If you try out this feature, please write about your experiences in the comments. I’m curious about your measurements and your opinion!