Cloud Computing 17 min read

Boost Java Cloud Run Performance: Proven JVM and Container Optimizations

Learn how to accelerate Java applications on Google Cloud Run by minimizing container image size, leveraging container-aware JVM settings, reducing thread usage, and applying Spring Boot-specific tweaks such as layered JARs, lazy initialization, and avoiding background tasks to cut startup latency and memory consumption.

Java Architecture Diary
Java Architecture Diary
Java Architecture Diary
Boost Java Cloud Run Performance: Proven JVM and Container Optimizations

Translator Note

This article originally describes Java optimization in Google Cloud Run; most recommendations also apply to Docker and Kubernetes containers.

Introduction

The guide presents Google’s recommendations for optimizing Java‑based Cloud Run services, especially Spring Boot, complementing the general optimization tips that also work for traditional Java applications.

Traditional Java web application optimization aims for high concurrency, low latency, and long‑running stability, with the JVM’s JIT and hotspot compilation improving efficiency.

Typical best practices include handling concurrent requests (thread‑based I/O or non‑blocking I/O) and using connection pools or background tasks to reduce response latency.

Many of these practices suit long‑running services but may be less effective for Cloud Run, which only runs while handling requests. The following sections cover Cloud Run‑specific trade‑offs for reducing startup time and memory usage.

Optimizing the Container Image

Reducing image size shortens load and startup times. Recommended actions:

Minimize the container image size.

Avoid using “fat” JARs that bundle dependencies.

Build images with Jib.

Minimize Image Size

Ensure the image does not contain source code, Maven build artifacts, build tools, Git directories, or unused binaries.

Consider using the Distroless Java base image, which is the default when building with Jib.

If you build from a Dockerfile, use multi‑stage builds so the final image contains only the JRE and the application JAR.

Avoid Fat JARs

Frameworks like Spring Boot create an “uber” JAR that must be unpacked at startup, slowing Cloud Run. Use Jib to create a slim JAR, or Spring Boot 2.3’s layered JAR feature.

Use Jib

The Jib plugin can produce minimal containers and automatically unpack dependencies. It supports Maven and Gradle and offers out‑of‑the‑box support for Spring Boot.

JVM Optimizations

Optimizing the JVM improves performance and memory usage.

Use a Container‑Aware JVM

Older JDKs read CPU and memory limits from

/proc

, which can cause thread‑pool oversizing and heap sizes that exceed container limits. Use a container‑aware JVM (OpenJDK 8u192 or newer) that reads limits from

/proc/cgroups

.

Understand JVM Memory Usage

JVM memory consists of native memory and heap memory. On a 256 MiB Cloud Run instance, not all memory can be allocated to the heap because the JVM and OS need native memory. Enable Native Memory Tracking to inspect usage on OOM events.

<code>java -XX:NativeMemoryTracking=summary \
  -XX:+UnlockDiagnosticVMOptions \
  -XX:+PrintNMTStatistics \
  ...</code>

Use a Java memory calculator or Alibaba’s Arthas to estimate native memory requirements.

Disable Optimizing Compiler

For short‑lived serverless workloads, consider disabling tiered compilation to reduce startup time:

<code>JAVA_TOOL_OPTIONS="-XX:+TieredCompilation -XX:TieredStopAtLevel=1"</code>

Disable Class Verification

If you trust the container image, you can disable verification to speed up startup:

<code>JAVA_TOOL_OPTIONS="-noverify"</code>

Note: This option is deprecated in OpenJDK 13+.

Reduce Thread Stack Size

Each Java thread consumes native memory (default 1 MiB). Reduce the stack size to lower memory usage, e.g.:

<code>JAVA_TOOL_OPTIONS="-Xss256k"</code>

Reduce Threads

Use non‑blocking reactive programming and avoid unnecessary background tasks to lower memory consumption.

Limit Thread Count

Cloud Run allows up to 80 concurrent requests. Configure the maximum threads, for example in Spring Boot:

<code>server.tomcat.max-threads=80</code>

Adopt Reactive Programming

Reactive frameworks (Spring WebFlux, Micronaut, Quarkus) can handle many concurrent requests with far fewer threads. Avoid writing blocking code in a reactive application.

Avoid Background Activities

When an instance receives no requests, Cloud Run limits CPU. Background tasks such as JDBC pool cleanup, metric batching, @Async methods, or message listeners may not run, leading to degraded behavior.

Application Optimizations

Reduce startup tasks and use connection pools wisely. For low QPS, consider opening and closing connections per request; for high QPS, ensure the total connections across instances stay within database limits.

Using Spring Boot

Prefer Spring Boot 2.2+ for built‑in startup improvements. Enable lazy initialization to defer bean creation:

<code>spring.main.lazy-initialization=true</code>

Or set the environment variable:

<code>SPRING_MAIN_LAZY_INITIATIALIZATION=true</code>

Avoid classpath scanning overhead by using the Spring Context Indexer:

<code>&lt;dependency&gt;
  &lt;groupId&gt;org.springframework&lt;/groupId&gt;
  &lt;artifactId&gt;spring-context-indexer&lt;/artifactId&gt;
  &lt;optional&gt;true&lt;/optional&gt;
&lt;/dependency&gt;
</code>

Do not package Spring Boot Devtools in production images; exclude or disable it.

Next Steps

For more tips, see the referenced articles on efficient Java optimization and migrating existing services.

References

General optimization tips: https://cloud.google.com/run/docs/tips

Minimize container image: https://cloud.google.com/run/docs/tips#minimize-container

Distroless Java base: https://github.com/GoogleContainerTools/distroless/tree/master/java

Jib plugin: https://cloud.google.com/java/getting-started/jib

Spring Boot 2.3 layered JAR: https://juejin.im/post/6844904167710916615

Jib GitHub: https://github.com/GoogleContainerTools/jib

Java memory calculator: https://github.com/cloudfoundry/java-buildpack-memory-calculator

Arthas: https://alibaba.github.io/arthas/

OpenJDK 13 deprecation note: https://www.oracle.com/java/technologies/javase/13all-relnotes.html

CPU limit on Cloud Run: https://cloud.google.com/run/docs/reference/container-contract#cpu-request

Avoid background tasks: https://cloud.google.com/run/docs/tips/java#background

Configure max instances: https://cloud.google.com/run/docs/configuring/max-instances

Manual Spring Boot optimizations: https://spring.io/blog/2018/12/12/how-fast-is-spring

Spring Boot Devtools: https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-devtools

Disable Spring Boot Devtools: https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-devtools

General Java optimization: https://cloud.google.com/run/docs/tips/general

Migrating services: https://cloud.google.com/run/docs/migrating

Original article: https://cloud.google.com/run/docs/tips/java#appcds

javaJVMDockerSpring BootContainer OptimizationCloud Run
Java Architecture Diary
Written by

Java Architecture Diary

Committed to sharing original, high‑quality technical articles; no fluff or promotional content.

0 followers
Reader feedback

How this landed with the community

login 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.