Adds spring-boot-startup-time/, the companion project for BLOG-618: a runnable Spring Boot 4.1.1 application on JDK 25 that installs BufferingApplicationStartup and FlightRecorderApplicationStartup behind a system property, and a /diag/startup endpoint that computes step self time -- the number /actuator/startup does not give you and the one that names the actual culprits. Captured under docs/output/: the step tree sorted both ways, the same startup as JFR events, a +5000-class experiment putting 0.11 ms per scanned class on the classpath scan tax, the silent truncation a 2048-step buffer performs, and JDK 25 AOT cache timings (6.93 s to 4.82 s). Post body and metadata live in post/. Moves the existing Actuator project into actuator-in-production/ so the repository holds one directory per article; the root README is now an index.
4.9 KiB
Why your Spring Boot app takes 8 seconds to start
Companion project for Why Your Spring Boot App Takes 8 Seconds to Start: A Bean-by-Bean Diagnosis.
Every millisecond figure in the article came out of this project. The transcripts are in
docs/output/ and are regenerated by one command.
Versions
Resolved by the build, not read from documentation — see
docs/output/00-versions.txt.
| Component | Version | Notes |
|---|---|---|
| Spring Boot | 4.1.1 | GA 20 August 2026 |
| Spring Framework | 7.0.9 | via spring-boot-starter-parent |
| Spring Data JPA | 4.1.1 | |
| Hibernate ORM | 7.4.5.Final | |
| Tomcat (embedded) | 11.0.24 | |
| Micrometer | 1.17.1 | |
| H2 | 2.4.240 | |
| JDK | Temurin 25.0.4.1+1 LTS | AOT cache needs 24+ |
| Maven | 3.9.11 |
Quickstart
export JAVA_HOME=/path/to/jdk-25
mvn -DskipTests package
# recording, and serving the self-time view at /diag/startup
java -Dstartup.tracking=buffering -jar target/startup-diagnosis-1.0.0.jar
curl -s 'localhost:8080/diag/startup?top=12' # self time -- the useful list
curl -s localhost:8080/actuator/startup # GET peeks
curl -sX POST localhost:8080/actuator/startup # POST drains. Only once.
Regenerate every transcript (~20 minutes):
JAVA_HOME=/path/to/jdk-25 ./scripts/run-all.sh
Switches
Tracking is a system property, not a Spring property, because ApplicationStartup must
be set before run(). See docs/02.
| Switch | Effect |
|---|---|
-Dstartup.tracking=buffering |
BufferingApplicationStartup (default in this project) |
-Dstartup.tracking=jfr |
FlightRecorderApplicationStartup; pair with -XX:StartFlightRecording |
-Dstartup.tracking=none |
no tracking — the uninstrumented baseline |
-Dstartup.buffer=2048 |
buffer capacity in steps; too small truncates silently |
--spring.profiles.active=lazy |
spring.main.lazy-initialization=true |
Endpoints
| Endpoint | What it gives you |
|---|---|
/diag/startup?top=N |
this project's own — self time, phase totals, both orderings |
/actuator/startup |
Boot's flat timeline. GET peeks, POST drains |
/actuator/health, /actuator/beans, /actuator/conditions |
exposed for context |
/orders/summary |
the application's actual job |
/diag/startup exposes bean names and wiring. Delete it before shipping.
Documentation
Captured output
| File | Scenario |
|---|---|
00-versions.txt |
resolved versions from the build |
01-api-corrections.txt |
javap and the compiler errors that corrected the code |
02-startup-tree.txt |
the step tree by self time and by total time |
03-jfr.txt |
the same startup as JFR events, read with jfr |
04-scan-tax.txt |
+5000 classes, with and without @Component |
05-what-helps.txt |
tracking overhead, lazy init, JDK 25 AOT cache |
06-buffer-overflow.txt |
what a 2048-step buffer silently loses |
Step counts and orderings are reproducible. Timings are indicative and drift by a few hundred milliseconds between runs on the same machine — the ratios are the result, not the absolute numbers.
Layout
pom.xml
scripts/
run-all.sh regenerate everything under docs/output/
run.sh / stop.sh start and stop; stop.sh matches java+jar, never the main class
gen-bulk.sh generate N classes into the scanned package
demo-*.sh one script per captured scenario
src/main/java/com/ankurm/startup/
slow/ four beans that do real work on the way up
domain/ one entity, one repository with derived queries
web/ StartupDiagnosticsEndpoint (self time) + the business controller
src/test/java/ contract tests for drain, truncation and double-counting
docs/ numbered chapters
docs/output/ captured real output
Licence
MIT — see LICENSE.