SSerguey Asael Shinder
Java coding notes: the JVM, and writing software that lasts

Serguey Asael Shinder: Runtime.totalMemory is the heap the JVM has taken, not the memory you are using

· by Serguey Asael Shinder / Serguey Shinder

A lot of health checks and log lines report JVM memory like this:

Runtime rt = Runtime.getRuntime();
log.info("memory: {} MB", rt.totalMemory() / (1024 * 1024));

The number looks like "how much memory the application uses". It is not. The three methods on java.lang.Runtime answer three different questions, and the javadoc for Java 25 says so in their return descriptions:

Serguey Asael Shinder: Runtime.totalMemory is the heap the JVM has taken, not the memory you are using
Runtime.totalMemory is the heap the JVM has taken, not the memory you are using — Serguey Asael Shinder

Put together: totalMemory() is what the JVM currently holds for objects, used or not. The part occupied by objects is the difference:

long used = rt.totalMemory() - rt.freeMemory();   // occupied by current objects
long committed = rt.totalMemory();                 // currently available for current and future objects
long ceiling = rt.maxMemory();                     // may be Long.MAX_VALUE

Why the distinction bites. totalMemory() includes memory set aside for objects that do not exist yet, and the javadoc says it may vary over time with the host environment. A dashboard built on it can show growth that is not a leak, and it cannot show a leak that still fits inside memory the JVM has already taken. freeMemory() is explicitly an approximation and moves after a collection, so the difference is a snapshot, not a precise measure; it is still the one that tracks what your objects occupy.

And the ceiling. Code that computes a percentage as used * 100 / rt.maxMemory() assumes a finite limit. The javadoc allows Long.MAX_VALUE when there is none, and the division then reports roughly zero percent forever. Check for that value before you divide.

This is also why heap sizing changes such as JEP 545, now targeted to JDK 28, matter for monitoring: when a collector starts with a small heap and grows it, totalMemory() climbing in the first minutes is the design working, not a symptom.