Slow Spring Boot startup hurts developer productivity and Kubernetes pod scheduling. Learn the key techniques to cut startup time - lazy initialization, GraalVM native, and class data sharing.
JOptimize Team
A Spring Boot app that takes 8 seconds to start is 8 seconds of blocked developer feedback, 8 seconds of Kubernetes readiness probe timeout, and 8 seconds of unavailability during rolling deploys. Startup time matters - and for most apps, it can be cut dramatically with targeted changes.
Enable Spring Boot's startup actuator endpoint to see exactly which beans take the longest:
spring.jmx.enabled=false logging.level.org.springframework.boot.autoconfigure=DEBUG
Or use the startup endpoint:
management.endpoints.web.exposure.include=startup
curl http://localhost:8080/actuator/startup | jq '.timeline.events | sort_by(.duration) | reverse | .[0:10]'
This shows the 10 slowest beans during startup - always start here before guessing.
By default, Spring initializes every bean at startup. Lazy initialization defers bean creation until first use:
# application.properties spring.main.lazy-initialization=true
Typical result: 40-60% startup time reduction. The trade-off is that the first request to each lazy bean takes slightly longer (one-time cost).
For specific beans that are known to be slow:
@Component @Lazy // Only initialized when first injected public class HeavyReportGenerator { // Complex initialization... }
Warning: Lazy init can hide startup errors. A misconfigured bean that would fail at startup now fails on first use. Run integration tests with lazy init disabled to catch these.
Spring Boot auto-configures hundreds of things. If you don't use them, exclude them:
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, // If no database SecurityAutoConfiguration.class, // If custom security FlywayAutoConfiguration.class, // If no migrations ThymeleafAutoConfiguration.class, // If pure REST API WebMvcAutoConfiguration.class // If using WebFlux }) public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
Or in properties:
spring.autoconfigure.exclude=\ org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\ org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration
AppCDS creates a shared archive of loaded classes, so subsequent JVM startups skip class loading:
# Step 1: Generate the archive (run once after build) java -XX:ArchiveClassesAtExit=app.jsa -jar app.jar # Step 2: Use the archive on every startup java -XX:SharedArchiveFile=app.jsa -jar app.jar
Spring Boot 3.3+ supports CDS out of the box:
# Spring Boot 3.3+ - built-in CDS support mvn spring-boot:process-aot # AOT processing mvn spring-boot:build-image # Includes CDS by default
Typical result: 20-35% startup improvement with no code changes.
Spring Boot 3.0+ supports AOT compilation, which moves runtime reflection work to build time:
<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <executions> <execution> <goals> <goal>process-aot</goal> </goals> </execution> </executions> </plugin>
mvn spring-boot:process-aot package java -Dspring.aot.enabled=true -jar app.jar
AOT pre-computes bean definitions and proxy classes at build time. Startup improvement: 15-25%.
For microservices where startup time is critical (serverless, Kubernetes with frequent scaling), GraalVM native image compiles your Spring Boot app to a native binary:
<plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> </plugin>
mvn -Pnative native:compile ./target/myapp # Starts in ~50-200ms
Before/After comparison (real Spring Boot app):
| Approach | Startup Time | Memory |
|---|---|---|
| Default JVM | 8.2s | 320MB |
| Lazy init | 3.1s | 310MB |
| Lazy + CDS | 2.2s | 295MB |
| AOT | 1.8s | 280MB |
| GraalVM Native | 0.12s | 55MB |
GraalVM caveats: Reflection-heavy frameworks, dynamic proxies, and runtime class loading need explicit configuration. Spring Boot 3.x has good native support, but third-party libraries may need hints.
# Add to application.properties for immediate improvement spring.main.lazy-initialization=true spring.jmx.enabled=false spring.mvc.pathmatch.use-suffix-pattern=false # Disable unused actuator endpoints management.endpoints.enabled-by-default=false management.endpoint.health.enabled=true
@PostConstruct methods - initialization logic in @PostConstruct runs synchronously during startup; defer it to ApplicationReadyEvent if it can waitMost Spring Boot startup time problems are solved by: enabling lazy initialization (biggest impact, easiest change), excluding unused auto-configurations, and using AppCDS. Spring AOT adds further improvement with minimal effort. Reserve GraalVM native image for services where sub-200ms startup is a hard requirement - it's powerful but adds build complexity.
JOptimize identifies heavy @PostConstruct methods, eagerly-loaded beans that should be lazy, and auto-configuration anti-patterns that slow down your Spring Boot startup.
Cut your Spring Boot startup time - free scan, no configuration required.
Master Spring Boot, security, and Java performance with hands-on courses.
JOptimize finds N+1 queries, EAGER collections, and 70+ other issues in your Java codebase — in under 30 seconds.