-Xmx and -Xms
-Xmx is the maximum heap size. -Xms is the initial size. On a server with a fixed amount of RAM, set both to the same value:
JAVA_OPTS="-Xms1536m -Xmx1536m"
The heap is then reserved once, the JVM does not spend time growing and shrinking it, and a memory problem shows up at startup instead of three days later.
MaxRAMPercentage
The JVM can read the memory of the machine or container and set the heap as a percentage of it:
-XX:MaxRAMPercentage=60 -XX:InitialRAMPercentage=60
This is useful in Docker, where the limit is set outside the application. On a VPS with a fixed size an explicit -Xmx is clearer. When both are set, -Xmx wins.
Metaspace
Class metadata lives in metaspace, outside the heap. By default it is unlimited, so a class loader leak can consume the server. Put a ceiling on it:
-XX:MaxMetaspaceSize=256m
Garbage collector
The default is G1 on a machine with at least two CPUs and 2 GB of memory. On smaller machines the JVM falls back to the Serial collector, which is a fine choice for a single vCPU. For large heaps with strict latency needs, consider ZGC (-XX:+UseZGC) on Java 21 and newer. Change the collector only after measuring a problem.
Failing fast on out-of-memory
An application that hits OutOfMemoryError is often in a bad state and keeps misbehaving. Let it exit and let systemd restart it:
-XX:+ExitOnOutOfMemoryError
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/app/
Keep an eye on disk space if you write heap dumps: a dump is as large as the live heap.
A starting point
For an 8 GB server with a database on it we start Spring Boot or Tomcat with this:
-Xms3600m -Xmx3600m -XX:MaxMetaspaceSize=256m -XX:+ExitOnOutOfMemoryError -Djava.awt.headless=true
That is 45% of RAM. For where the number comes from, read how much RAM a Java app needs.
Last updated October 29, 2026.