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.
JOptimize detects this automatically
Scan your Java project in 30 seconds — free, no credit card.
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.
JOptimize finds N+1 queries, EAGER collections, and 70+ other issues in your Java codebase — in under 30 seconds.