Files
spring-boot-demo/spring-boot-startup-time/docs/01-the-number-boot-logs.md
Ankur Mhatre 958b401f0f Spring Boot startup time: bean-by-bean diagnosis, and one directory per post
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.
2026-09-05 00:17:37 +05:30

2.4 KiB

01 — The number Boot logs, and what it hides

next → 02 — Turning instrumentation on


Every Spring Boot application ends its startup with one line:

Started StartupDiagnosisApplication in 6.6 seconds (process running for 7.4)

Two numbers. The first is measured from the SpringApplication.run() call to the ApplicationReadyEvent. The second is ManagementFactory.getRuntimeMXBean().getUptime(), so the gap between them is JVM bootstrap: opening the jar, verifying classes, starting the JIT — work that happens before your code runs at all.

Neither number tells you where the 6.6 seconds went, and the usual next move — reading the log timestamps — is worse than it looks. The log only shows you components that chose to log. This application spends about half a second inside tariffCacheWarmer and logs nothing while doing it. On the timeline it is a silent gap between two Hibernate lines, and you would reasonably conclude that Hibernate was slow.

What is actually available

Spring Framework has carried an ApplicationStartup SPI since 5.3. It is a tracing interface with one method that matters:

public interface ApplicationStartup {
    StartupStep start(String name);
}

Framework and Boot call it at about two dozen named points — spring.beans.instantiate, spring.context.config-classes.parse, spring.data.repository.proxy, and so on — tagging each step with the bean name or class count involved. Steps nest, so what you get is a tree, not a list.

The default implementation, DefaultApplicationStartup, does nothing. That is the whole reason startup profiling feels unavailable: the instrumentation is already in your application and is switched off.

Two implementations record it:

Implementation Where it lives Output
BufferingApplicationStartup spring-boot in-memory buffer, read over HTTP
FlightRecorderApplicationStartup spring-core JFR events in a .jfr file

There is no third option and no third-party agent needed.

What this repository measures

The application here is deliberately ordinary: web, JPA over H2, three repositories, and four beans that do real work on the way up. It starts in about 6.6 seconds on the machine that produced docs/output/. Every figure in the article and in these chapters came from a file in that directory.


next → 02 — Turning instrumentation on