Quick answer
The Java heap is only one part of a JVM process. Diagnose memory by reconciling heap committed and used, metaspace and class metadata, code cache, thread stacks, direct and native buffers, GC structures, JFR and agents, shared libraries, process RSS, container working set, and the cgroup limit. A container can be OOM-killed without the JVM throwing a Java heap OutOfMemoryError.
Java 25 Native Memory Tracking reports HotSpot-managed native categories and supports baseline comparisons. NMT does not track all native memory: Oracle documents that it excludes third-party native code and some JDK class-library allocation. Compare NMT with operating-system and container evidence instead of forcing the numbers to equal RSS.
Learning objectives
- Reconcile managed heap, HotSpot native categories, process residence, and container limits without treating reserved address space as resident memory.
- Use NMT baseline and diff evidence safely while recognizing the native allocations it cannot see.
Prerequisites
Read Java Heap Dumps and Memory Leak Analysis and Kubernetes Resource Requests, Limits, and JVM Containers so heap ownership and container capacity remain separate concerns.
Evidence and system boundary
Reserved memory is virtual address space; committed memory has backing promised by the operating system; resident memory is currently present in physical memory. Their definitions and reporting differ. Container working-set metrics can subtract reclaimable cache, while RSS tools and cgroup counters expose different views. Preserve the metric definition before comparing numbers.
NMT is off by default and must be enabled at JVM startup. These are explanatory Java 25 examples, not an executed production policy:
java -XX:NativeMemoryTracking=summary -jar order-service.jar
jcmd <pid> VM.native_memory baseline
jcmd <pid> VM.native_memory summary.diff scale=MB
Oracle documents a performance and memory overhead for NMT. Choose summary or detail before startup, bound the observation, and canary the overhead. NMT can be stopped but cannot be started or restarted on a running JVM.
Reconcile the memory budget
Begin with the cgroup memory limit and the process peak, not -Xmx alone. Reserve headroom for heap, metaspace, code cache, thread stacks, GC bookkeeping, native libraries, direct buffers, JIT compiler state, observability agents, and workload bursts. CPU throttling can prolong memory ownership by slowing collection and request completion.
Use NMT categories to test a HotSpot hypothesis. Java Heap shows reserved and committed heap; Class includes class metadata; Thread includes native thread structures and stacks; Code covers generated code; GC covers collector structures; Compiler covers compilation; and Internal or Other can require deeper investigation. A baseline diff is more useful than one large static total when investigating growth.
If RSS grows but NMT totals and heap remain stable, consider third-party native allocation, memory-mapped files, libc behavior, allocator fragmentation, JDK library allocation outside NMT coverage, or the metric definition. Use approved operating-system tools for that boundary. Do not label the unexplained remainder a leak without a trend and owner.
Different failures produce different evidence. java.lang.OutOfMemoryError: Java heap space concerns managed allocation. Metaspace, Direct buffer memory, or unable to create native thread indicate different resources. A container OOMKill is an external kernel decision and may leave only platform events, exit status, and cgroup evidence.
Production failure scenario
A Spring order service container limit is 2 GiB while -Xmx is 1.7 GiB. Heap use remains stable, but RSS approaches the limit as platform-thread stacks and direct HTTP buffers grow. Kubernetes reports an OOMKill; no Java heap exception is recorded.
NMT baseline diff shows thread and HotSpot categories, while OS evidence accounts for additional native buffers. The team reduces unnecessary platform-thread creation, bounds direct-buffer demand, and restores container headroom in a canary. Recovery requires stable RSS, no new OOMKills, normal checkout latency, and durable order verification—not a green heap graph alone.
Common misconceptions
-Xmxis not the container memory budget.- Reserved address space is not necessarily committed or resident memory.
- NMT does not report every third-party or JDK class-library native allocation.
- A container OOMKill does not require a preceding Java
OutOfMemoryError. - Reducing heap can create GC pressure even when it prevents one container kill; the whole budget must be tested.
Decision checklist
- Record cgroup limit, JVM flags, collector, process peak, and metric definitions.
- Budget non-heap memory explicitly before setting
-Xmx. - Enable NMT only through an approved startup policy and measure its overhead.
- Establish a warm baseline before interpreting
summary.diffordetail.diff. - Compare NMT with RSS, cgroup, OOM events, and native-library ownership.
- Distinguish heap, metaspace, direct-buffer, native-thread, and external OOM failures.
- Canary heap, thread, buffer, or container changes independently.
- Verify user outcomes and durable orders after memory pressure clears.
Related reading
Continue with JVM Thread Dumps, Safepoints, and CPU Saturation. Use the Cloud-Native Backend course for container capacity, and follow the canonical Java Concurrency course plus topic cluster.
Official sources
- Java 25 Native Memory Tracking
- Java 25 diagnostic tools
- Java 25 java command
- Kubernetes resource management
Official sources accessed August 19, 2026. Verify NMT coverage, overhead, container runtime metrics, and the exact JVM update before relying on a memory total.