← Recipes

Container or Kubernetes Service

Size the JVM from container limits and make CPU or memory ergonomics visible.

Flags

-XX:MaxRAMPercentage-XX:InitialRAMPercentage-XX:MaxDirectMemorySize-XX:ActiveProcessorCount-Xlog:gc*-Xlog:os

-XX:MaxRAMPercentage caps heap as a percentage of the memory available to the JVM, but heap is not the whole pod footprint. Direct buffers, metaspace, thread stacks, code cache, GC internals, agents, and native libraries also count against the container limit. -XX:ActiveProcessorCount overrides the CPU count used by GC, compiler, and common pool ergonomics when quota detection or pod sizing needs an explicit value.

Flag details

-XX:InitialRAMPercentage=50
Sets the initial heap as a percentage of detected available memory. In a container, that should be the cgroup memory limit when container support is active.
-XX:MaxRAMPercentage=75
Sets the maximum heap as a percentage of detected available memory. Leave space for non-heap memory, especially direct buffers, thread stacks, metaspace, native libraries, and monitoring agents.
-XX:MaxDirectMemorySize=256m
Caps memory used by direct byte buffers. This is important for Netty, NIO, gRPC, Kafka clients, database drivers, and other I/O-heavy libraries because direct memory lives outside the Java heap.
-XX:ActiveProcessorCount=2
Overrides the CPU count used by JVM ergonomics. This affects GC thread counts, compiler behavior, and APIs such as Runtime.availableProcessors().
-Xlog:gc*
Records allocation pressure, heap occupancy, and GC time. Use this before changing collector or heap policies so the tuning is based on observed behavior.
-Xlog:os=info
Prints OS-level runtime details. In containerized deployments, use this with startup settings and platform metrics to confirm what memory and CPU limits the JVM sees.

When to use it

Use this for Docker and Kubernetes workloads where pod memory is the real boundary. Leave headroom for metaspace, direct buffers, thread stacks, JIT code cache, native libraries, and the process itself.

BellSoft's memory guide calls out the central trap: heap sizing flags do not control total JVM memory consumption. In Kubernetes, the safe budget is the pod memory limit minus non-heap JVM memory and native process overhead.

A common starting point is 60-70% heap in a container, then adjust after observing RSS, direct memory, and GC behavior. Use higher percentages only after proving non-heap headroom is stable.

Container security posture

JVM memory flags are only one part of a safe Kubernetes launch. Pair them with platform controls that reduce blast radius and avoid leaking sensitive material through process arguments or diagnostics.

Java version support

The percentage-based RAM flags are intended for modern container-aware JVMs and are most relevant on Java 10+. -XX:MaxDirectMemorySize and -XX:ActiveProcessorCount are JVM options rather than language features. -Xlog examples require Java 9+.

Related JEPs

Examples

java -XX:InitialRAMPercentage=50 -XX:MaxRAMPercentage=70 \
  -Xlog:gc*,os=info \
  -jar app.jar

Use this when the pod memory limit should drive heap sizing.

java -XX:MaxRAMPercentage=65 -XX:MaxDirectMemorySize=256m \
  -Xlog:gc*,os=info \
  -jar reactive-api.jar

Use this for I/O-heavy services where direct buffers can grow outside the heap.

java -XX:MaxRAMPercentage=65 -XX:ActiveProcessorCount=2 \
  -Xlog:os=trace \
  -jar app.jar

Use this when a small CPU quota needs explicit JVM ergonomics.

Verify

References

See BellSoft's Guide to JVM memory configuration options for memory flags and Oracle's Java command documentation for Java 25 option syntax.