Java
Programming
Memory Management
Command Line Options
-Xmx

What does Java option -Xmx stand for?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

-Xmx sets the maximum heap size for the Java Virtual Machine (JVM). The "X" prefix means it is a non-standard (implementation-specific) option, and "mx" stands for "maximum memory." When you write -Xmx512m, you are telling the JVM that the heap may grow up to 512 megabytes but never beyond that. If the application tries to allocate more objects than will fit in that limit, the JVM throws java.lang.OutOfMemoryError: Java heap space.

How to Use -Xmx

Pass -Xmx as a command-line argument when launching a Java application:

bash
java -Xmx1024m -jar application.jar

The size suffix determines the unit:

bash
java -Xmx512k -jar app.jar   # 512 kilobytes (rarely useful)
java -Xmx512m -jar app.jar   # 512 megabytes
java -Xmx2g -jar app.jar     # 2 gigabytes

The suffixes are case-insensitive (m and M both work). If no suffix is provided, the value is interpreted as bytes.

-Xmx vs -Xms vs -Xss

These three options control different memory regions. They are independent and commonly confused.

OptionControlsDefault (HotSpot)Example
-XmxMaximum heap size1/4 of physical RAM (capped at various limits)-Xmx4g
-XmsInitial heap sizeVaries by JVM version and ergonomics-Xms512m
-XssThread stack size512KB to 1MB depending on OS-Xss1m

Setting -Xms equal to -Xmx is a common production practice. It prevents the JVM from spending time resizing the heap during the warmup phase:

bash
java -Xms4g -Xmx4g -jar application.jar

What Happens When -Xmx Is Too Low

When the heap reaches the -Xmx limit and garbage collection cannot free enough space, the JVM throws an OutOfMemoryError:

java
1// Deliberately exhaust heap to demonstrate OOM
2import java.util.ArrayList;
3import java.util.List;
4
5public class OomDemo {
6    public static void main(String[] args) {
7        List<byte[]> leak = new ArrayList<>();
8        while (true) {
9            leak.add(new byte[1_000_000]); // 1 MB per iteration
10        }
11    }
12}
bash
java -Xmx64m OomDemo
# Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

What Happens When -Xmx Is Too High

Setting -Xmx much larger than the application needs is not free. A very large heap means garbage collection pauses can be longer because the GC has more memory to scan. On a machine with limited physical RAM, a large -Xmx can also cause swapping, which degrades performance severely.

The right approach is profiling, not guessing. Start with a reasonable value, monitor heap usage under realistic load, and adjust.

How to Choose the Right Value

There is no universal formula, but these guidelines cover most cases:

For microservices and small applications (Spring Boot APIs, CLI tools): start at 256m to 512m and monitor.

For mid-size applications (web servers handling moderate traffic): 1g to 4g is typical.

For data-intensive applications (batch processing, in-memory caches): size the heap to hold the working dataset plus overhead for GC. Profile with realistic data.

For containers (Docker, Kubernetes): the JVM must be aware of the container's memory limit. Modern JVMs (Java 10+) detect container memory automatically, but always verify:

bash
# In a container with 2GB memory limit
java -XX:+PrintFlagsFinal -version 2>&1 | grep MaxHeapSize

On Java 8u191+, use -XX:+UseContainerSupport (enabled by default in later builds). On older Java 8, the JVM reads the host's physical RAM instead of the container limit, which can cause it to set -Xmx far too high and get OOM-killed by the kernel.

JVM Memory Beyond the Heap

The heap is only one part of JVM memory. A complete picture includes:

text
1Total JVM memory =
2    Heap (-Xmx)
3  + Metaspace (-XX:MaxMetaspaceSize, unlimited by default)
4  + Thread stacks (-Xss * number of threads)
5  + Direct byte buffers (-XX:MaxDirectMemorySize)
6  + Code cache (-XX:ReservedCodeCacheSize)
7  + GC overhead
8  + JNI / native allocations

This means a process with -Xmx512m can easily consume 700-800MB of RSS. When sizing containers, add at least 256MB of headroom above -Xmx for non-heap memory.

Inspecting Heap Usage at Runtime

Several tools can show current heap usage without restarting the application:

bash
1# Show heap configuration and usage
2jcmd <pid> GC.heap_info
3
4# Show all JVM flags including -Xmx
5jcmd <pid> VM.flags
6
7# Classic alternative
8jstat -gc <pid> 1000

For deeper analysis, use a profiler like VisualVM, Java Flight Recorder (JFR), or async-profiler to see which objects are consuming heap space:

bash
1# Start with JFR recording
2java -Xmx2g \
3     -XX:StartFlightRecording=duration=60s,filename=recording.jfr \
4     -jar application.jar

Setting -Xmx in Build Tools and Servers

Different contexts have different ways to configure JVM options:

bash
1# Maven
2export MAVEN_OPTS="-Xmx1g"
3mvn package
4
5# Gradle
6org.gradle.jvmargs=-Xmx2g   # in gradle.properties
7
8# Tomcat (setenv.sh)
9export CATALINA_OPTS="-Xmx4g -Xms4g"
10
11# Spring Boot with environment variable
12export JAVA_TOOL_OPTIONS="-Xmx1g"
13java -jar app.jar

JAVA_TOOL_OPTIONS is recognized by all JVMs and is useful when you cannot modify the launch command directly (e.g., in container orchestration).

Relationship to Garbage Collection

The choice of GC algorithm affects how -Xmx behaves in practice:

GC AlgorithmFlagBest Heap RangeNotes
G1 GC (default since Java 9)-XX:+UseG1GC2g to 64gBalances throughput and latency
ZGC-XX:+UseZGC8g to multi-TBSub-millisecond pauses, higher memory overhead
Shenandoah-XX:+UseShenandoahGC2g to 128gLow-pause, similar to ZGC
Parallel GC-XX:+UseParallelGC1g to 16gThroughput-oriented, longer pauses
Serial GC-XX:+UseSerialGCUnder 256mSingle-threaded, for small heaps

With G1 GC, the JVM subdivides the heap into regions and collects incrementally. Larger heaps do not necessarily mean longer pauses. With ZGC, pauses stay under 1ms regardless of heap size, making very large -Xmx values practical for in-memory workloads.

Common Pitfalls

Confusing -Xmx with total process memory. The JVM uses significantly more memory than just the heap. Metaspace, thread stacks, direct buffers, and native allocations all add up. Setting -Xmx to the container's full memory limit guarantees OOM kills.

Not setting -Xmx explicitly in production. The JVM's ergonomic default (1/4 of physical RAM) may be too large or too small. Always set it explicitly for predictable behavior.

Using old Java 8 in containers without UseContainerSupport. Before Java 8u191, the JVM did not respect cgroup memory limits and would set -Xmx based on the host's total RAM, often leading to the container being killed.

Setting -Xms much smaller than -Xmx. This forces the JVM to resize the heap repeatedly during startup, causing GC pauses and slower warmup. In production, setting them equal is usually better.

Ignoring thread stack memory. Each thread consumes -Xss worth of stack space (default around 1MB). An application with 500 threads uses roughly 500MB of stack space alone, on top of the heap.

Summary

  • -Xmx sets the maximum heap size for the JVM. The "X" means non-standard, "mx" means maximum memory.
  • Specify the value with a suffix: k for kilobytes, m for megabytes, g for gigabytes.
  • -Xms sets the initial heap size, -Xss sets the thread stack size. They are independent of -Xmx.
  • Too low causes OutOfMemoryError. Too high causes longer GC pauses and potential swapping.
  • In containers, verify the JVM detects the memory limit correctly (Java 10+ does this by default).
  • Total JVM memory is always larger than -Xmx. Add headroom for metaspace, stacks, and native memory.
  • Profile with real workloads rather than guessing. Use jcmd, jstat, or JFR to monitor heap usage.

Course illustration
Course illustration

All Rights Reserved.