This article details how to significantly reduce Spring Boot application startup times within Docker containers by leveraging JVM's Class Data Sharing (CDS) feature. It explains that by pre-parsing and archiving Java classes during the Docker build process, the repetitive and time-consuming class loading at runtime is minimized, directly impacting application readiness and autoscaling efficiency. This technique offers a practical solution to a common performance bottleneck in microservices deployments without the complexities of native image compilation.
Read original on DZone MicroservicesA common issue in cloud-native environments, particularly with Spring Boot applications on Kubernetes, is the extended startup time of new pods. While a container might launch quickly, the application itself can take many additional seconds to become ready to serve traffic. During this period, existing pods bear increased load, latency rises, and aggressive autoscaling can exacerbate the problem by adding more unready pods, leading to cascading failures or performance degradation. This directly impacts system resilience and responsiveness, especially during traffic spikes.
The primary culprit for slow Spring Boot startup is the JVM's class loading process. A typical Spring Boot application loads thousands of classes, each requiring the JVM to find, read, parse, verify, and build internal metadata structures. This computationally intensive work is repeated identically for every single application startup, across all pods, even though the application's bytecode remains unchanged within an immutable Docker image. This repetition represents a significant waste of resources and time.
Class Data Sharing (CDS)
Class Data Sharing (CDS) is a JVM feature that allows pre-parsed and verified Java classes to be stored in a shared archive file (.jsa). On subsequent JVM startups, this archive can be memory-mapped directly, bypassing the expensive class loading ritual. Modern JDKs use CDS for core JDK classes, and this article extends the concept to application-specific classes.
The article demonstrates a multi-stage Docker build process to integrate CDS. The key steps involve: 1. Exploding the JAR: Unpacking the Spring Boot "fat" JAR into a directory structure with dependencies laid out as plain files. This ensures a stable classpath for CDS. 2. Training Run: Executing the Spring Boot application once during the build process with the `-XX:ArchiveClassesAtExit` flag. Spring Boot 3.3+ simplifies this with `spring.context.exit=onRefresh`, allowing the application to initialize its context, load classes, and then exit cleanly without requiring external infrastructure (like databases). This run generates the `.jsa` archive. 3. Runtime Configuration: The final Docker image includes the generated `.jsa` archive, and the application is configured to use it via the `-XX:SharedArchiveFile` JVM option.
FROM eclipse-temurin:21-jdk-alpine AS build
WORKDIR /build
COPY . .
RUN ./mvnw -B package -DskipTests
RUN java -Djarmode=tools -jar target/app.jar extract --destination /app
FROM eclipse-temurin:21-jre-alpine AS runtime
WORKDIR /app
COPY --from=build /app /app
RUN java -Dspring.context.exit=onRefresh \
-XX:ArchiveClassesAtExit=/app/app.jsa \
-jar /app/app.jar
ENV JAVA_TOOL_OPTIONS="-XX:SharedArchiveFile=/app/app.jsa"
ENTRYPOINT ["java", "-jar", "/app/app.jar"]This approach reduces startup times by roughly 50% for typical Spring Boot services. It offers benefits over GraalVM native images by preserving the standard JVM runtime and ecosystem (debugging, profiling, libraries) while significantly improving a key performance metric crucial for highly available and scalable microservice architectures.