A Spring Boot 4.1.1 order service started 12 ways (plain and extracted jar, Spring AOT output, AppCDS, AOT cache trained with and without traffic, JDK 27, native image), ten interleaved rounds each, with first-request latency; 24 cache-mismatch cases; Docker layer arithmetic. Transcripts are in output/ (no docs/ folder). Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01TF9JWFvJSNm6HVzswzZU5a
aot-cache: the JDK 25 AOT cache on a Spring Boot 4.1 service
Companion project for Faster Spring Boot Startup with the JDK AOT Cache (JEP 514/515): Benchmarks vs CDS and Native
on ankurm.com. Everything the article quotes is a file in output/, regenerated by
run.sh. The explanation lives in the article; this directory holds the code and the raw transcripts.
There is no docs/ folder on purpose: the deeper material sits in collapsible sections of the article itself.
The service
A small Spring Boot 4.1.1 web application, built to have the ingredients of a typical service and no more:
Spring MVC and Jackson (POST /orders with a JSON body), Bean Validation (a record with @Email, @Pattern,
@Min/@Max), the actuator health endpoint, and an in-memory store. No database. Sources are under
src/main/java/com/ankurm/aotcache/.
Versions
| Spring Boot | 4.1.1 (Spring Framework 7.0.9, Tomcat 11.0.24, Jackson 3.1.5, Hibernate Validator 9.1.3) |
| JDK for the article | Temurin 25.0.4.1 (LTS) |
| JDK for the comparison | Temurin 27+35 |
| GraalVM (native image) | Community Edition 25.0.2+10.1 |
| Machine | 2 vCPU, 8 GB, Linux |
What is here
| Script | What it does | Output |
|---|---|---|
scripts/prepare.sh |
Builds the plain jar, a jar with Spring's own AOT output, a jar after a one-line change; extracts them | 00-prepare.txt, 00-environment.txt |
scripts/native.sh |
Compiles the GraalVM native image | 03-native-build.txt |
scripts/train.sh |
The training runs: AppCDS, AOT cache (context-only and with traffic, with and without Spring AOT, on JDK 27, under ZGC) | 01-training.txt |
scripts/startup.sh |
12 configurations x 10 interleaved rounds: time to ready, Boot's own figure, first request, resident memory | 02-startup.txt |
scripts/mismatch.sh |
24 ways the cache and the run can disagree, and what the JVM says and does | 04-mismatch.txt |
scripts/layers.sh |
Docker layer arithmetic without Docker: which layers change on a one-line edit, sizes, gzipped sizes | 05-layers.txt |
scripts/sample.sh |
A transcript of the service's endpoints | 06-sample-endpoints.txt |
docker/Dockerfile |
A layered image with the cache trained in the final stage (not built here: no Docker daemon) |
Headline result
Median of ten runs, milliseconds from starting the process to the first 200 from GET /ping
(output/02-startup.txt):
| Configuration | Ready | First POST /orders |
|---|---|---|
| Plain JVM, extracted jar | 3,920 | 173 |
| AppCDS | 2,583 | 164 |
| AOT cache, context-only training | 2,022 | 143 |
| AOT cache, training with traffic | 1,949 | 68 |
| AOT cache + Spring AOT, training with traffic | 1,436 | 89 |
| GraalVM native image | 133 | 5.3 |
Running it
Needs JDK 25, JDK 27, a GraalVM for JDK 25 and Maven; scripts/env.sh shows where they are looked up and each can be
overridden with JDK25, JDK27 and GRAALVM. The measurement scripts assume Linux and curl, and need the machine to
themselves.
cd aot-cache
./run.sh # about 35 minutes; the native build is 5 of them
ROUNDS=3 ./run.sh # a quick pass
Caveats
- One small service on one 2-CPU virtual machine. The ratios are what to take away, not the milliseconds.
- No database and no JPA, so the service is smaller than most real ones; a larger classpath usually gains more from the cache.
- JEP 515 (method profiles) is part of the cache on JDK 25 and cannot be switched off separately, so its own contribution was not isolated.
- The Docker image was not built; the layer figures come from extracting the jar's layers and comparing files.
openjdk.org/jepsreturned HTTP 403 to the tooling used, so JEP claims come from running the JDKs and from secondary sources named in the article.