# jdk28-zgc — what ZGC startup and heap sizing look like today, and what JEP 545 / 546 propose Companion module for the ankurm.com post **ZGC in JDK 28: Adaptive Heap Sizing (JEP 546) and Faster Startup (JEP 545)**. There is no `docs/` folder: this README and the captured files in [`output/`](output) are the documentation. ## The honest status On 12 October 2026 both JEPs show **"Proposed to Target"** for release 28 on openjdk.org. Neither is integrated. [`output/01-flag-probe.txt`](output/01-flag-probe.txt) proves it: `-XX:+ZAdaptiveHeapSizing` is an *Unrecognized VM option* on JDK 25.0.4.1, Temurin 27+35 and OpenJDK 28-ea+19. So this module does **not** benchmark the proposed features. It measures the baseline they are meant to improve, on three JDKs, so you can rerun it the day a build contains the flag. ## Versions | Component | Version | |---|---| | JDK 25 | Temurin 25.0.4.1+1 (LTS) | | JDK 27 | Temurin 27+35 (GA 2026-09-15) | | JDK 28 | OpenJDK 28-ea+19 (jdk.java.net build 19, 2026-10-08) | | Spring Boot | 4.1.1 (`spring-boot-starter-webmvc`) | | Machine | 2 vCPU, 8 GB RAM Linux sandbox (a small box: absolute numbers are illustrative) | ## Quick start ```console mvn -B -DskipTests package # build with any JDK 25+ export J25=/path/to/jdk-25 J27=/path/to/jdk-27 J28=/path/to/jdk-28-ea scripts/run-all.sh # regenerates everything in output/ RUNS=10 scripts/run-all.sh # more runs, tighter medians scripts/startup.sh $J28 "my label" 5 -XX:+UseZGC -Xms1g ``` ## Files | File | What it is | |---|---| | [`src/main/java/com/ankurm/zgc/DemoApplication.java`](src/main/java/com/ankurm/zgc/DemoApplication.java) | One server: `/hello` allocates 16 KB, `/heap` reports committed and max heap | | [`scripts/startup.sh`](scripts/startup.sh) | Wall-clock ms from process start to first HTTP 200, repeated, with min/median/max | | [`scripts/warmup.sh`](scripts/warmup.sh) + [`scripts/Warmup.java`](scripts/Warmup.java) | 20,000 requests, 4 threads, timing each batch of 2,000; client is always JDK 25 | | [`scripts/run-all.sh`](scripts/run-all.sh) | Runs all three experiments | ## Results (median of 5 runs, milliseconds to first response) | Config | JDK 25 | JDK 27 | JDK 28-ea | |---|---|---|---| | ZGC defaults | 3439 | 3421 | 3420 | | `-Xms64m` | 3299 | 3276 | 3350 | | `-Xms1g` | 3478 | 3275 | 3642 | | `-Xms3g` | 3836 | 3618 | 3803 | | `-Xms3g -XX:+AlwaysPreTouch` | 4834 | 4560 | 5010 | Raw per-run lines: [`output/02-startup-vs-xms.txt`](output/02-startup-vs-xms.txt). Warmup, total ms for the first 20,000 requests (first 2,000 in brackets): | Config | JDK 25 | JDK 28-ea | |---|---|---| | defaults | 9708 (2678) | 9912 (2567) | | `-Xms3g` | 9193 (2606) | 9608 (2431) | | `-Xms64m` | 9247 (2442) | 9893 (2590) | Raw: [`output/03-warmup.txt`](output/03-warmup.txt). ## What the numbers say, and do not say - A bigger initial heap costs startup time, and `AlwaysPreTouch` costs the most (about +1.2 s to +1.6 s on a 3 GB heap here). - On this small box the effect is a fraction of Spring Boot's own ~3.3 s startup. JEP 545 describes a much larger effect on big heaps (about 0.1 s at 2 MB versus over 10 s at 32 GB). This machine cannot reproduce that, and these numbers do not test it. - The first batch of 2,000 requests takes about 2.4 to 2.7 s in every configuration, so for this app warmup is dominated by JIT and class loading, not heap growth. - Under ZGC, `InitialHeapSize` ergonomics are 98,566,144 bytes on JDK 25 but 8,388,608 bytes on JDK 27 and 28-ea ([`output/01-flag-probe.txt`](output/01-flag-probe.txt)). I did not find which change causes that, so the post reports it as an observation only. - Timings drift between runs. Treat them as indicative, not as a result. ## Licence MIT, as the parent repository.