Cloud Native 19 min read

Shrink Java Docker Images & Halve Startup Time Using OpenJ9 & GraalVM

This article demonstrates how Java applications can be made smaller, faster to start, and more resource‑efficient in the cloud‑native era by using multi‑stage Docker builds, OpenJ9’s shared class cache and AOT compilation, and GraalVM native images, with step‑by‑step examples based on the Spring PetClinic and Micronaut projects.

Programmer DD
Programmer DD
Programmer DD
Shrink Java Docker Images & Halve Startup Time Using OpenJ9 & GraalVM

The arrival of the cloud‑native era prompts Java developers to ask whether Java still has a place; the author argues that Java can remain a "giant" by applying a series of experiments that improve size, startup speed, and resource usage.

Cloud‑Native Runtime Requirements

Smaller image size : reduces download bandwidth and speeds distribution.

Faster startup : critical for micro‑services, rapid iteration, and serverless cold‑starts.

Lower runtime resource consumption : enables higher deployment density and lower cost.

Horizontal scalability : smaller footprints allow many replicas per node.

Warm‑up Preparation

Most Spring developers are unfamiliar with the techniques needed to make a Java application smaller and faster. The classic Spring PetClinic demo is used as a testbed.

$ git clone https://github.com/denverdino/adopt-openj9-spring-boot
$ cd adopt-openj9-spring-boot

First, a Docker image is built with an OpenJDK base, Maven, and the compiled PetClinic JAR:

FROM adoptopenjdk/openjdk8
RUN sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/' /etc/apt/sources.list
RUN apt-get update && apt-get install -y git maven
WORKDIR /tmp
RUN git clone https://github.com/spring-projects/spring-petclinic.git
WORKDIR /tmp/spring-petclinic
RUN mvn install
WORKDIR /tmp/spring-petclinic/target
CMD ["java","-jar","spring-petclinic-2.1.0.BUILD-SNAPSHOT.jar"]

Building and running this image yields a 871 MB container—much larger than the 300 MB base image—because build‑time tools and temporary files are included.

Image Slimming with Multi‑Stage Build

Docker’s multi‑stage build separates the build environment from the runtime image, dramatically reducing size:

FROM adoptopenjdk/openjdk8 AS build
RUN sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/' /etc/apt/sources.list
RUN apt-get update && apt-get install -y git maven
WORKDIR /tmp
RUN git clone https://github.com/spring-projects/spring-petclinic.git
WORKDIR /tmp/spring-petclinic
RUN mvn install
FROM adoptopenjdk/openjdk8:jre8u222-b10-alpine-jre
COPY --from=build /tmp/spring-petclinic/target/spring-petclinic-2.1.0.BUILD-SNAPSHOT.jar .
CMD ["java","-jar","spring-petclinic-2.1.0.BUILD-SNAPSHOT.jar"]

The resulting image shrinks to 167 MB, improving distribution speed.

From JIT to AOT – Startup Boost

OpenJ9 provides a Shared Class Cache (SCC) and Ahead‑of‑Time (AOT) compilation. By pre‑warming the SCC during the build, the container can start much faster:

FROM adoptopenjdk/openjdk8-openj9 AS build
RUN sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/' /etc/apt/sources.list && apt-get update && apt-get install -y git maven
WORKDIR /tmp
RUN git clone https://github.com/spring-projects/spring-petclinic.git
WORKDIR /tmp/spring-petclinic
RUN mvn install
FROM adoptopenjdk/openjdk8-openj9:jre8u222-b10_alpine
COPY --from=build /tmp/spring-petclinic/target/spring-petclinic-2.1.0.BUILD-SNAPSHOT.jar .
# Pre‑warm SCC and AOT
RUN /bin/sh -c 'java -Xshareclasses -Xquickstart -jar spring-petclinic-2.1.0.BUILD-SNAPSHOT.jar &' && sleep 20 && pkill -9 java
CMD ["java","-Xshareclasses","-Xquickstart","-jar","spring-petclinic-2.1.0.BUILD-SNAPSHOT.jar"]

Startup time drops from ~8.2 s (HotSpot) to ~4 s, a ~50 % improvement. Memory usage also falls dramatically when using OpenJ9 (average ~120 MB vs ~600 MB for HotSpot).

Native Code Compilation with GraalVM & Micronaut

To eliminate the JVM altogether, the article builds a Micronaut version of PetClinic and compiles it to a native binary with GraalVM:

# Build stage
FROM maven:3.6.1-jdk-8 as build
COPY ./ /micronaut-petclinic/
WORKDIR /micronaut-petclinic
RUN mvn package
# GraalVM stage
FROM oracle/graalvm-ce:19.2.0 as graalvm
RUN gu install native-image
WORKDIR /work
COPY --from=build /micronaut-petclinic/target/micronaut-petclinic-*.jar .
RUN native-image --no-server -cp micronaut-petclinic-*.jar
# Final lightweight image
FROM frolvlad/alpine-glibc
WORKDIR /app
COPY --from=graalvm /work/petclinic .
EXPOSE 8080
CMD ["/app/petclinic"]

The native image starts in ~159 ms—about 1/50 of the HotSpot startup time—demonstrating the potential of compile‑time optimization for serverless workloads.

Summary and Outlook

Java continues to evolve in the cloud‑native era. OpenJ9 offers substantial gains in startup speed and memory footprint while remaining fully compatible with existing Spring micro‑services. Micronaut combined with GraalVM takes a different route by moving much of the dynamic behavior to compile time, achieving near‑instant startup suitable for serverless platforms. Both approaches illustrate valuable design ideas for radical simplification across the software stack.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaDockerPerformance OptimizationgraalvmOpenJ9
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.