JEP 483: Ahead-of-Time Class Loading & Linking

"mprove startup time by making the classes of an application instantly available, in a loaded and linked state, when the HotSpot Java Virtual Machine starts. Achieve this by monitoring the application during one run and storing the loaded and linked forms of all classes in a cache for use in subsequent runs. Lay a foundation for future improvements to both startup and warmup time."

🧠 JEP 483: Ahead-of-Time Class Loading & Linking

🔍 What is it?

JEP 483 introduces a new way to pre-link Java classes during application build or testing — before your app even starts.

It extends the existing CDS (Class Data Sharing) system by allowing the JVM to record and cache class linking steps, like:

  • Bytecode verification

  • Constant pool resolution

  • Method/field linking

📜 CDS (Class Data Sharing) was first introduced in:

Java 5 (JDK 1.5), released in 2004.


✅ Quick Timeline

Version
Feature/Change

Java 5 (2004)

CDS introduced for core JDK classes only (boot classpath)

Java 9 (2017)

AppCDS added: support for user-defined classes in the archive

Java 13+

CDS improved with support for dynamic archiving (recording class list at runtime)

Java 21+

Continuous improvements in heap object archiving, layered archives, and module support

Java 24 (JEP 483)

Ahead-of-Time class linking cache added as a separate optimization

🛠 How it works (in 3 steps)

  1. Record linking behavior during app execution -XX:AOTMode=record

  2. Create a binary cache of that data -XX:AOTMode=create

  3. Run your app with that prelinked cache -XX:AOTMode=on -XX:AOTCache=...

DEMO

run :

scripts/jep483/run-aot-classloading.sh 

Logs:

[0.267s][info][cds,aot,link] app com.wlodar.jeeps.jep483aotclassloading.MiniApp 0xa001800
[0.267s][info][cds,aot,link] app com.wlodar.jeeps.jep483aotclassloading.Service 0xa001a18
[0.267s][info][cds,aot,link] boot1 java.nio.HeapCharBuffer 0xa009cf8
[0.267s][info][cds,aot,link] app com.wlodar.jeeps.jep483aotclassloading.Repository 0xa001c40
[0.267s][info][cds,aot,link] app com.wlodar.jeeps.jep483aotclassloading.Controller 0xa002000
[0.267s][info][cds,aot,link] app com.wlodar.jeeps.jep483aotclassloading.Formatter 0xa002228

[0.427s][info][cds,aot,link] wrote 889 class(es) for category boot1
[0.427s][info][cds,aot,link] wrote 5 class(es) for category app
AOTCache creation is complete: target/app.aot

....

[0.151s][info][cds,aot,load] app   com.wlodar.jeeps.jep483aotclassloading.MiniApp
[0.155s][info][cds,aot,load] app   com.wlodar.jeeps.jep483aotclassloading.Service
[0.155s][info][cds,aot,load] app   com.wlodar.jeeps.jep483aotclassloading.Repository
[0.155s][info][cds,aot,load] app   com.wlodar.jeeps.jep483aotclassloading.Controller
[0.155s][info][cds,aot,load] app   com.wlodar.jeeps.jep483aotclassloading.Formatter

app.aotconf

com/wlodar/jeeps/jep483aotclassloading/MiniApp id: 563
com/wlodar/jeeps/jep483aotclassloading/Service id: 578
com/wlodar/jeeps/jep483aotclassloading/Repository id: 579
com/wlodar/jeeps/jep483aotclassloading/Controller id: 580
com/wlodar/jeeps/jep483aotclassloading/Formatter id: 581
@cp com/wlodar/jeeps/jep483aotclassloading/Formatter 1 2 8 15 16 21 22 27 48
@cp com/wlodar/jeeps/jep483aotclassloading/Controller 1 2 7 9 10 11 17 24 25 30 34 53
@cp com/wlodar/jeeps/jep483aotclassloading/Repository 1 2 8 15 16
@cp com/wlodar/jeeps/jep483aotclassloading/Service 1 2 7 9 10 11 16 18 19 24 31 32 37 41
@cp com/wlodar/jeeps/jep483aotclassloading/MiniApp 8 15 16 21 23 24
@cp com/wlodar/jeeps/jep483aotclassloading/MiniApp 8 15 16 21 23 24

Says:

Inside MiniApp.class, the JVM accessed constant pool entries #8, #15, #16, etc., during execution.

What each of those indexes actually refer to depends on the compiled .class file.

javap -v target/classes/com/wlodar/jeeps/jep483aotclassloading/MiniApp.class

 #8 = Class              #10            // java/lang/System
   #9 = NameAndType        #11:#12        // out:Ljava/io/PrintStream;
  #10 = Utf8               java/lang/System
  #11 = Utf8               out
  #12 = Utf8               Ljava/io/PrintStream;
  #13 = String             #14            // 📦 Starting MiniApp...
  #14 = Utf8               📦 Starting MiniApp...
  #15 = Methodref          #16.#17        // java/io/PrintStream.println:(Ljava/lang/String;)V
  #16 = Class              #18            // java/io/PrintStream
...
  #21 = Class              #22            // com/wlodar/jeeps/jep483aotclassloading/Service
  #22 = Utf8               com/wlodar/jeeps/jep483aotclassloading/Service
  #23 = Methodref          #21.#3         // com/wlodar/jeeps/jep483aotclassloading/Service."<init>":()V
  #24 = Methodref          #21.#25        // com/wlodar/jeeps/jep483aotclassloading/Service.run:()V

...

📦 What is app.aot?

The file created by:

bashCopyEditjava \
  -XX:AOTMode=create \
  -XX:AOTConfiguration=app.aotconf \
  -XX:AOTCache=app.aot

is a compiled AOT cache — a binary file that contains:

✅ Pre-resolved class linking and constant pool data:

  • Class-level metadata

  • Constant pool resolutions

  • Method handles and symbolic references

  • Resolved field/method/class names

  • Potentially some intermediate compiler structures

✅ Purpose:

This allows the JVM to:

  • Skip the linking and symbol resolution phase

  • Avoid reflection costs at startup

  • Preload certain method metadata and types before the app even begins


🧠 Analogy:

Think of app.aotconf as the blueprint, and app.aot as the compiled cache based on that blueprint.

File
Purpose
Human-readable?

app.aotconf

Text list of classes & CP indexes used

✅ Yes

app.aot

Binary cache for AOT linking

❌ No (JVM only)


💡 When is app.aot used?

When you run with:

bashCopyEditjava \
  -XX:AOTMode=on \
  -XX:AOTCache=app.aot \
  -cp ...
  com.example.Main

➡️ The JVM loads the .aot file at startup, checks the current heap layout, and links in the pre-recorded class resolutions without doing it live.

Last updated