skip to content

What do the Java command-line options -Xms and -Xmx set, and what does the HotSpot JVM choose for them when you specify neither?

level: juniorimportance: must knowfreq 55%

answer

  1. -Xms initial and floor, -Xmx ceiling
  2. defaults roughly 1/64 and 1/4 of available memory
  3. container-aware by default since JDK 10
  4. reserved vs committed
  5. -Xmx bounds the heap, not the process

basics

~20 s

-Xms is the initial heap size the JVM commits at startup; -Xmx is the maximum it may grow to. With neither set, HotSpot defaults to roughly 1/64 of available memory for initial and 1/4 for maximum, reading the container limit rather than host RAM when running under one.

solid answer

~50 s

-Xms sets the initial, and effectively minimum, committed heap; -Xmx sets the upper bound. Between them the JVM grows and shrinks the heap as demand changes, committing more operating-system memory when occupancy after collection is high. With neither flag, HotSpot's default ergonomics pick about 1/64 of available memory as the initial size and 1/4 as the maximum - equivalently -XX:InitialRAMPercentage around 1.56 and -XX:MaxRAMPercentage 25. Since JDK 10, container support is on by default, so 'available memory' means the cgroup memory limit when one is set, not the physical RAM of the host. Two things to be clear about. These bound the Java heap only; metaspace, code cache, thread stacks and direct buffers live outside it, so process memory is always larger than -Xmx. And when the live set genuinely does not fit under -Xmx after a full collection, the JVM throws OutOfMemoryError rather than growing further.

code

text · 8 lines
text
# absolute
java -Xms2g -Xmx2g -jar app.jar

# percentage of the container/cgroup limit (scales with the limit)
java -XX:InitialRAMPercentage=60 -XX:MaxRAMPercentage=60 -jar app.jar

# what did the JVM actually pick?
java -XX:+PrintFlagsFinal -version | grep -E 'InitialHeapSize|MaxHeapSize'

go deeper

for a junior

Know which flag is initial and which is maximum, the rough default fractions, and that they cover only the heap.

for a middle

Add reserved versus committed, the free-ratio-driven growth, and the percentage flags for containers.

for a senior

Set them from a measured live set and an explicit non-heap budget rather than accepting ergonomic defaults.

for a principal

Standardise how services express heap size across the fleet - percentage-based in containers, absolute where the instance is fixed - so limits stay coherent when instance sizes change.

## The two bounds The Java heap is the region the garbage collector manages, holding all objects and arrays. Its size is bounded at both ends by command-line options that have kept their non-standard names for decades. -Xms sets the initial heap: the amount the JVM reserves and commits at startup. In practice it also acts as a floor, because HotSpot will not shrink the committed heap below it. -Xmx sets the maximum: the ceiling beyond which the heap will not grow. Both accept suffixes, so -Xms512m and -Xmx4g are typical spellings. Newer equivalents express the same thing as fractions of available memory: -XX:InitialRAMPercentage and -XX:MaxRAMPercentage, which are the better choice in containers because they scale when the limit changes. ## Growth and shrinkage between them When a collection ends and too little of the heap is free, the JVM commits more memory, up to -Xmx. When far too much is free, it may return memory, down to -Xms. HotSpot's classic controls for this are -XX:MinHeapFreeRatio and -XX:MaxHeapFreeRatio (40 and 70 by default in the collectors that use them): keep free space between those percentages after a collection, expanding or shrinking to get there. It is worth separating two words. Reserved memory is address space the JVM claimed up front, typically the whole of -Xmx. Committed memory is what is actually backed by the operating system and counts toward the process footprint. Growing the heap means committing more of the already-reserved range. ## The defaults If you pass neither flag, HotSpot chooses using its ergonomics. On a server-class machine the rule of thumb is initial about 1/64 of available memory and maximum about 1/4, with small-memory adjustments at the low end. The important modern detail is what 'available memory' means: since JDK 10 the JVM is container-aware by default (-XX:+UseContainerSupport), so inside a container with a memory limit it reads the cgroup limit rather than the host's physical RAM. Before that, a JVM in a 512 MB container on a 64 GB host would happily size its heap for 64 GB and be killed by the kernel. A default maximum of 25% is deliberately conservative and is usually wrong for a dedicated service container, where three quarters of the memory you paid for would sit unused. Setting -XX:MaxRAMPercentage explicitly - or a fixed -Xmx - is the normal production practice. ## What these flags do not cover A very common misunderstanding is that -Xmx caps the memory of the Java process. It does not. Outside the heap the process also holds class metadata in metaspace, JIT-compiled code in the code cache, one stack per thread, direct and mapped byte buffers, garbage-collector bookkeeping structures, and whatever native libraries allocate. A process with -Xmx1g routinely uses well over 1.3 GB of resident memory. Any memory budget - a container limit, a capacity plan - has to account for all of it. ## When the ceiling is reached If the application's reachable data cannot fit under -Xmx, the collector will run repeatedly, reclaim little, and eventually the JVM reports OutOfMemoryError for the heap. Raising -Xmx is the right response only when the live set is legitimately that large; otherwise it postpones the same failure. Note also that the JVM refuses to start if -Xms exceeds -Xmx, and that an -Xms larger than the machine or container can supply fails at startup rather than later. ## Practical starting point For a dedicated container, decide the total budget, subtract a realistic non-heap allowance, and set the heap explicitly - either as a fixed -Xmx or as a percentage - rather than relying on a 25% default that was chosen for shared desktop machines.

  • Does -Xmx limit how much memory the Java process uses in total?
    No. It limits only the garbage-collected heap. Metaspace, the JIT code cache, per-thread stacks, direct byte buffers, GC bookkeeping and native library allocations all sit outside it, so resident memory is always meaningfully larger than -Xmx and has to be budgeted separately.
  • Why prefer -XX:MaxRAMPercentage over a fixed -Xmx in a container?
    Because it is expressed relative to the cgroup memory limit, the same image behaves sensibly when the limit is raised or lowered without editing the command line. It also removes the risk of an -Xmx that silently exceeds a reduced container limit, which would let the heap grow into a kernel OOM kill.

saying these in an interview costs you the question

  • Saying -Xmx caps total process memory
  • Assuming a JVM in a container sizes its heap from host RAM on modern JDKs
  • Thinking the JVM commits all of -Xmx at startup regardless of -Xms
  • Believing the default maximum heap is a fixed number of megabytes rather than a fraction of available memory
  • Setting -Xms larger than -Xmx and expecting it to be clamped rather than rejected

context