Java 26 + Project Leyden

AOT Cache Comparison — Plain Spring Boot vs PetClinic

Does complexity amplify startup gains? Let’s test it.


The Question I Wanted to Answer

We all know Spring Boot apps can be slow to start.

But with Java 26 introducing AOT Object Caching via Project Leyden, I wanted to answer a very specific question:

👉 Does AOT caching help more as application complexity increases?

To test this, I compared:

  • A plain Spring Boot app
  • A real-world Spring Boot app (PetClinic)

What is AOT Cache in Java 26?

With Leyden, the JVM can:

  1. Run your app once (training run)
  2. Capture initialized objects into a cache
  3. Reuse them in future runs

👉 This avoids repeating expensive initialization like:

  • Bean creation
  • Dependency injection
  • Reflection

Applications Used

1️⃣ Plain Spring Boot App

Using Spring Boot minimal setup:

@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}

👉 Characteristics:

  • Minimal beans
  • Basic auto-configuration
  • No database

2️⃣ Real-World App: PetClinic

Using Spring PetClinic

👉 Characteristics:

  • Controllers + Services
  • Hibernate / JPA
  • Database initialization
  • Complex dependency graph

How I Tested

Each application was tested in 2 modes ( normal run vs AOT Cache enabled run ):

🔹 Normal Run

java -cp target/spring-petclinic-4.0.0-SNAPSHOT.jar org.springframework.samples.petclinic.PetClinicApplication

🔹 First Run (Cache Record)

java -XX:AOTMode=record -XX:AOTConfiguration=target/aotconf -cp target/spring-petclinic-4.0.0-SNAPSHOT.jar org.springframework.samples.petclinic.PetClinicApplication

👉 This run builds the cache (slightly slower)


🔹 Second Run (Cache Creation)

java -XX:AOTMode=create -XX:AOTConfiguration=target/aotconf -XX:AOTCache=app.aot -cp target/spring-petclinic-4.0.0-SNAPSHOT.jar org.springframework.samples.petclinic.PetClinicApplication
🔹 Third Run (Cache Usage)
java -XX:AOTCache=app.aot -cp app.jar org.springframework.samples.petclinic.PetClinicApplication


👉 This is where the real benefit appears


Results

ApplicationNormal StartupWith AOT CacheImprovement
Plain Spring Boot~2.4 sec~1.6 sec⚡ ~33%
PetClinic~6.8 sec~3.1 sec🚀 ~54%

Visual Comparison

Startup Time ↓
PetClinic ████████████ → ██████
Plain Spring ██████ → ████

Key Insight

AOT Cache benefits grow with application complexity

  • Plain Spring Boot → moderate improvement
  • PetClinic → significant improvement

👉 The heavier the initialization, the bigger the win.


Why PetClinic Gains More

PetClinic does a lot more work during startup:

Plain Spring Boot:

  • Component scanning
  • Basic bean creation

PetClinic:

  • Entity scanning
  • Hibernate setup
  • Database initialization
  • Deep dependency injection graph

👉 Leyden caches all of this:

  • Initialized beans
  • Dependency graph
  • Reflection results

So instead of rebuilding:

👉 The JVM reuses a ready-to-go state


Best Fit: AWS Lambda & Kubernetes CronJobs

This is where Project Leyden becomes extremely practical.


🔹 1. AWS Lambda (Cold Start Problem)

Using Amazon Web Services Lambda with Java has always had one big issue:

👉 Cold start latency

  • JVM startup
  • Spring context initialization
  • Dependency injection

⏱️ This can take seconds, which impacts:

  • API latency
  • User experience

💡 How Leyden Helps

With AOT Cache:

  • Pre-initialized Spring context
  • Faster JVM startup
  • Reduced cold start time

👉 Result:

Significantly faster Lambda cold starts without rewriting your app


⚠️ Important Note for Lambda

  • Cache reuse depends on execution environment reuse
  • Not as deterministic as containers
  • Still beneficial, but results may vary

🔹 2. Kubernetes CronJobs (Perfect Use Case)

Using Kubernetes CronJobs:

👉 Pattern:

  • Start container
  • Run job
  • Exit

🚨 The Problem

Most of the time is wasted in:

  • Spring Boot startup
  • Bean initialization

Instead of actual job execution


💡 Why Leyden is Perfect Here

  • Startup cost happens every run
  • Same code runs repeatedly
  • Cache can be reused across executions (if image is reused properly)

👉 Result:

Huge reduction in job execution time


🧠 Real Example

Imagine:

  • Job runs for 10 seconds
  • Startup takes 5 seconds

With Leyden:

  • Startup drops to ~2 seconds

👉 Total runtime improves significantly (~30–40%)


Where This Fits

ApproachStartupComplexityFlexibility
Normal JVMSlowLowHigh
Spring AOTMediumMediumHigh
Leyden AOT CacheFastLowHigh
Native ImageFastestHighLow

When Should You Use This?

✅ Best suited for:

  • Serverless workloads (Lambda)
  • Batch jobs (CronJobs)
  • Microservices scaling frequently

❌ Less impact for:

  • Long-running monoliths
  • Apps with rare restarts

Final Thoughts

This experiment clearly shows:

Project Leyden is not about small apps—it’s about real-world systems and runtime efficiency.

For simple apps:

  • Gains are modest

For complex systems like PetClinic:

  • Gains are substantial

For cloud workloads:

  • Gains can directly impact cost and performance

Recommendation

If you’re using Spring Boot in:

  • AWS Lambda
  • Kubernetes CronJobs
  • Microservices

👉 Start experimenting with AOT Cache now

Leave a comment