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:
- Run your app once (training run)
- Capture initialized objects into a cache
- 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:
@SpringBootApplicationpublic 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
| Application | Normal Startup | With AOT Cache | Improvement |
|---|---|---|---|
| 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
| Approach | Startup | Complexity | Flexibility |
|---|---|---|---|
| Normal JVM | Slow | Low | High |
| Spring AOT | Medium | Medium | High |
| Leyden AOT Cache | Fast | Low | High |
| Native Image | Fastest | High | Low |
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