Add jdk27-perf: the same Spring Boot service measured on JDK 25 and JDK 27
Live heap, throughput, p99 and startup across four configurations, including opt-in and opt-out of compact object headers. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01WzVQ6zFh9cuLPj9RSbRH8U
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
# jdk27-perf — what actually got faster in JDK 27, measured on one Spring Boot service
|
||||
|
||||
Companion module for the ankurm.com post **What Actually Got Faster in JDK 27: Measured on a Spring Boot App**
|
||||
(<https://ankurm.com/what-actually-got-faster-in-jdk-27-measured-spring-boot/>).
|
||||
No `docs/` folder: this README and the raw files in [`output/`](output) are the documentation.
|
||||
|
||||
## Question
|
||||
|
||||
JDK 27 changed two defaults that matter to a server: compact object headers are on (JEP 534) and G1 is the default collector everywhere (JEP 523).
|
||||
Does the same Spring Boot service get faster, smaller, or neither when you move from JDK 25 to JDK 27 with no flags?
|
||||
|
||||
## Setup
|
||||
|
||||
| Thing | Value |
|
||||
|---|---|
|
||||
| JDK 25 | Temurin 25.0.4.1+1 (LTS) |
|
||||
| JDK 27 | Temurin 27+35 (GA 2026-09-15) |
|
||||
| Spring Boot | 4.1.1, `spring-boot-starter-webmvc` |
|
||||
| Machine | 2 vCPU, 8 GB RAM Linux sandbox (the load client shares the box, so absolute numbers are modest and noisy) |
|
||||
| Service | 3,000,000 resident `Item(int id, int qty)` records; `/quote?n=200` builds 200 response records; Jackson serialises them |
|
||||
| Load | closed loop, 4 client threads, 15 s warmup discarded, 30 s measured, client always JDK 25 |
|
||||
| Repetitions | 3 per configuration, interleaved so machine drift hits all four equally |
|
||||
|
||||
Four configurations: `jdk25 defaults`, `jdk25 +CompactObjectHeaders` (opt in), `jdk27 defaults`, `jdk27 -CompactObjectHeaders` (opt out).
|
||||
[`output/01-defaults.txt`](output/01-defaults.txt) shows both JDKs pick G1 on this machine, so the collector is the same everywhere and only headers and the rest of the JDK differ.
|
||||
|
||||
## Quick start
|
||||
|
||||
```console
|
||||
mvn -B -DskipTests package # build with JDK 25 or later
|
||||
export J25=/path/to/jdk-25 J27=/path/to/jdk-27
|
||||
scripts/run-all.sh # about 12 minutes; regenerates output/
|
||||
REPS=5 scripts/run-all.sh # more repetitions
|
||||
```
|
||||
|
||||
## Results
|
||||
|
||||
Medians of 3 runs ([`output/03-summary.txt`](output/03-summary.txt), raw blocks in [`output/02-runs.txt`](output/02-runs.txt)):
|
||||
|
||||
| Config | Startup ms | Throughput req/s (range) | p99 ms |
|
||||
|---|---|---|---|
|
||||
| jdk25 defaults | 3437 | 6788 (6239-6868) | 3.23 |
|
||||
| jdk25 +CompactObjectHeaders | 3512 | 7157 (6407-7228) | 3.09 |
|
||||
| jdk27 defaults | 3220 | 7016 (6755-7275) | 2.99 |
|
||||
| jdk27 -CompactObjectHeaders | 3388 | 7010 (6867-7227) | 3.03 |
|
||||
|
||||
Resident data set after a forced full GC ([`output/04-live-set.txt`](output/04-live-set.txt), identical in all three repetitions):
|
||||
|
||||
| Config | Live heap MiB |
|
||||
|---|---|
|
||||
| jdk25 defaults | 93 |
|
||||
| jdk25 +CompactObjectHeaders | 69 |
|
||||
| jdk27 defaults | 70 |
|
||||
| jdk27 -CompactObjectHeaders | 93-94 |
|
||||
|
||||
## What it says
|
||||
|
||||
- **Footprint is the clear win.** Compact headers cut the live heap by 23 to 24 MiB (whole-MiB rounding). That matches 3,000,000 objects times 8 bytes saved, which is 22.9 MiB. A two-int record goes from 24 to 16 bytes.
|
||||
- **Speed is not distinguishable.** The throughput ranges overlap in every row and p99 differs by fractions of a millisecond. On this box I cannot claim JDK 27 is faster.
|
||||
- **Startup is inconclusive.** JDK 27 defaults had the lowest median, but the individual runs overlap with JDK 25.
|
||||
- Resident set size and committed heap vary run to run with G1's sizing decisions; see the per-run `/stats` lines in `02-runs.txt` before drawing a conclusion from them.
|
||||
|
||||
## Honest notes
|
||||
|
||||
- `output/02-runs.txt` was produced by the first build of the jar. The `/live` endpoint used for `04-live-set.txt` was added afterwards; it does not touch the `/quote` path, but strictly the two files come from two builds of this module.
|
||||
- Timings drift. Treat them as indicative.
|
||||
|
||||
## Licence
|
||||
|
||||
MIT, as the parent repository.
|
||||
Reference in New Issue
Block a user