Cloud Native 18 min read

Shrink Java Docker Images and Boost Startup Speed with OpenJ9, Micronaut, and GraalVM

This article explores why Java faces challenges in the cloud‑native era, demonstrates how multi‑stage Docker builds, OpenJ9 shared class cache, and Micronaut‑GraalVM native compilation can dramatically reduce image size and cut startup time by up to 50 % while also lowering memory consumption.

Alibaba Cloud Native
Alibaba Cloud Native
Alibaba Cloud Native
Shrink Java Docker Images and Boost Startup Speed with OpenJ9, Micronaut, and GraalVM

Why Java Needs Cloud‑Native Optimisation

Java remains dominant in enterprise software, but its runtime memory footprint and CPU usage often earn it the nicknames “memory‑eater” and “CPU‑tear‑er”. In a micro‑service world, smaller image size, faster start‑up, and lower resource consumption are critical for rapid iteration, horizontal scaling, and serverless workloads.

Docker Image Bloat with a Standard Build

Using the classic Spring PetClinic example, a Docker image built on adoptopenjdk/openjdk8 with Maven and Git ends up at 871 MB , far larger than the 300 MB base image. The excess comes from build‑time dependencies (Git, Maven) and temporary files that are not needed at runtime.

$ git clone https://github.com/denverdino/adopt-openj9-spring-boot
$ cd adopt-openj9-spring-boot
$ cat Dockerfile.openjdk
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"]

Multi‑Stage Build for Image Slimming

Docker’s multi‑stage build separates the build environment from the runtime image, copying only the compiled JAR into a lightweight JRE base. The resulting image shrinks from 871 MB to 167 MB .

$ cat Dockerfile.openjdk-slim
FROM adoptopenjdk/openjdk8 AS build
RUN ... (install git, maven, build the app) ...
FROM adoptopenjdk/openjdk8:jre8u222‑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"]

Accelerating Startup with OpenJ9 AOT and SCC

OpenJ9 provides a Shared Class Cache (SCC) and Ahead‑of‑Time (AOT) compilation. By enabling -Xshareclasses and -Xquickstart during the build, the JVM pre‑compiles bytecode and stores the native code in a memory‑mapped cache. The cache is baked into the final Docker image, allowing the container to start in roughly 4 seconds instead of the original 8 seconds.

$ cat Dockerfile.openj9.warmed
FROM adoptopenjdk/openjdk8‑openj9 AS build
... (install git, maven, build the app) ...
FROM adoptopenjdk/openjdk8‑openj9:jre8u222‑openj9‑0.15.1‑alpine
COPY --from=build /tmp/spring-petclinic/target/spring-petclinic‑2.1.0.BUILD‑SNAPSHOT.jar .
RUN /bin/sh -c 'java -Xscmx50M -Xshareclasses -Xquickstart -jar spring-petclinic‑2.1.0.BUILD‑SNAPSHOT.jar &' && sleep 20 && pkill -1 java
CMD ["java","-Xscmx50M","-Xshareclasses","-Xquickstart","-jar","spring-petclinic‑2.1.0.BUILD‑SNAPSHOT.jar"]

Memory usage drops from ~600 MB (HotSpot) to ~120 MB (OpenJ9) when running four containers side‑by‑side, demonstrating a ~80 % reduction.

Micronaut + GraalVM Native Compilation

Micronaut moves dependency injection to compile time, eliminating most reflection. Combined with GraalVM’s native‑image, the Spring PetClinic can be compiled to a native executable that starts in ≈159 ms , roughly 1/50 of a HotSpot launch.

$ git clone https://github.com/denverdino/micronaut-petclinic
$ cd micronaut-petclinic
$ cat Dockerfile
FROM maven:3.6.1‑jdk‑8 AS build
COPY . /micronaut-petclinic/
WORKDIR /micronaut-petclinic
RUN mvn package
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
FROM frolvlad/alpine‑glibc
EXPOSE 8080
WORKDIR /app
COPY --from=graalvm /work/petclinic .
CMD ["/app/petclinic"]

The native image runs with negligible memory overhead and near‑instant start‑up, making it attractive for serverless deployments.

Takeaways

In the cloud‑native era, Java can stay competitive by:

Using multi‑stage Docker builds to strip build‑time tools and shrink images.

Leveraging OpenJ9’s SCC and AOT to pre‑warm the JVM and cut start‑up time and memory usage.

Adopting compile‑time frameworks like Micronaut and native‑image tooling (GraalVM) for ultra‑fast, low‑memory binaries.

These techniques illustrate how traditional Java workloads can be re‑engineered for modern, container‑first environments without abandoning the rich Java ecosystem.

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.

JavaperformanceDockergraalvmMicronautOpenJ9
Alibaba Cloud Native
Written by

Alibaba Cloud Native

We publish cloud-native tech news, curate in-depth content, host regular events and live streams, and share Alibaba product and user case studies. Join us to explore and share the cloud-native insights you need.

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.