Native memory: why RSS beats -Xmx
The heap is one pot among several. A container kills you on the total, not on -Xmx.
Open this lesson in the learning hubKey points
- A Java process is the heap plus metaspace, code cache, GC structures, thread stacks, direct buffers and the JDK itself.
- Thread stacks are native memory. 400 threads at the default 1 MB
-Xssis 400 MB that nobody put in the budget. - Direct
ByteBuffers and memory-mapped files sit outside the heap entirely. Netty and NIO allocate plenty of them. - Switch on
-XX:NativeMemoryTracking=summaryand read it withjcmd <pid> VM.native_memory summary. - A kernel OOM-kill is
SIGKILL: exit code 137, no stack trace, no heap dump. It is not anOutOfMemoryError. - Rule of thumb: leave a quarter of the container limit outside the heap, which is exactly what
MaxRAMPercentage=75does.
Example
import java.lang.management.BufferPoolMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryPoolMXBean;
import java.lang.management.MemoryType;
import java.nio.ByteBuffer;
public class Main {
public static void main(String[] args) {
Runtime rt = Runtime.getRuntime();
ByteBuffer direct = ByteBuffer.allocateDirect(16 * 1024 * 1024); // 16 MB, off the heap
direct.putInt(0, 42);
System.out.println("the heap - the only part -Xmx controls");
System.out.printf(" max %d MB, used %d MB%n",
rt.maxMemory() >> 20, (rt.totalMemory() - rt.freeMemory()) >> 20);
System.out.println();
System.out.println("everything else the process is holding, none of it inside -Xmx:");
for (MemoryPoolMXBean pool : ManagementFactory.getMemoryPoolMXBeans()) {
if (pool.getUsage() == null || pool.getType() == MemoryType.HEAP) { continue; }
System.out.printf(" %-38s %8d KB%n", pool.getName(), pool.getUsage().getUsed() >> 10);
}
for (BufferPoolMXBean pool : ManagementFactory.getPlatformMXBeans(BufferPoolMXBean.class)) {
System.out.printf(" %-38s %8d KB (%d live)%n",
"buffers: " + pool.getName(), pool.getMemoryUsed() >> 10, pool.getCount());
}
System.out.println();
System.out.println("Thread stacks add -Xss for every thread on top of all of this,");
System.out.println("and the container limit applies to the sum, not to the heap.");
System.out.println("checksum " + direct.getInt(0));
}
}
Size the container for the whole process, not for the heap.
This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the JVM course, and every lesson in it is listed on the JVM contents page.