Unlock Hidden JVM Memory: Track Native Allocations with NMT
This tutorial explains why Java applications often exceed their -Xmx limits, enumerates the main sources of native memory consumption in the JVM, and shows how to monitor and analyze these allocations using Native Memory Tracking (NMT) commands and flags.
1. Overview
Ever wondered why a Java application using the well‑known -Xms and -Xmx flags consumes more memory than the specified limits? The JVM can allocate additional native memory for various reasons, causing the actual consumption to exceed the -Xmx ceiling. This tutorial lists common native memory sources in the JVM, the tuning flags that control their size, and how to monitor them with native memory tracking.
2. Native Allocations
Besides the heap, the JVM allocates sizable native memory blocks for class metadata, application code, JIT‑generated code, internal data structures, and more.
2.1 Metaspace
The JVM uses a dedicated non‑heap region called Metaspace (formerly PermGen) to store metadata about loaded classes. Its size is independent of the heap and is controlled by flags such as -XX:MetaspaceSize and -XX:MaxMetaspaceSize (or -XX:PermSize and -XX:MaxPermSize on Java 8 and earlier).
2.2 Threads
Each thread has its own stack, typically about 1 MB on modern 64‑bit OSes, configurable via -Xss. The total stack memory can become unbounded if the number of threads is not limited.
2.3 Code Cache
The JIT compiler stores compiled machine code in a non‑heap region called the Code Cache. Its size is controlled by -XX:InitialCodeCacheSize and -XX:ReservedCodeCacheSize.
2.4 Garbage Collection
All GC algorithms use additional native data structures, which also consume native memory.
2.5 Symbols
String interning stores a single copy of each distinct string in a native hash table (the String Table). The size of this table can be tuned with -XX:StringTableSize. The runtime constant pool, which holds literals and method/field references, also resides in native memory.
2.6 Native Byte Buffers
Developers can allocate native memory directly via JNI malloc or NIO direct ByteBuffer s.
2.7 Additional Tuning Flags
Various other -XX flags control specific native memory areas; you can discover them with commands like:
$ java -XX:+PrintFlagsFinal -version | grep <concept>3. Native Memory Tracking (NMT)
Enable NMT with the flag -XX:NativeMemoryTracking=off|summary|detail. By default it is off; set it to summary or detail to view aggregated or detailed reports.
Example for a typical Spring Boot application:
$ java -XX:NativeMemoryTracking=summary -Xms300m -Xmx300m -XX:+UseG1GC -jar app.jarUse jcmd <pid> VM.native_memory to obtain a snapshot. Find the PID with jps -l:
$ jps -l
7858 app.jar // This is our app
7899 sun.tools.jps.JpsSample output sections:
3.1 Total Allocation
Native Memory Tracking:
Total: reserved=1731124KB, committed=448152KBReserved memory is the maximum the JVM might use; committed memory is what is currently in use.
3.2 Heap
Java Heap (reserved=307200KB, committed=307200KB)
(mmap: reserved=307200KB, committed=307200KB)3.3 Metaspace
Class (reserved=1091407KB, committed=45815KB)
(classes #6566)
(malloc=10063KB #8519)
(mmap: reserved=1081344KB, committed=35752KB)3.4 Threads
Thread (reserved=37018KB, committed=37018KB)
(thread #37)
(stack: reserved=36864KB, committed=36864KB)
(malloc=112KB #190)
(arena=42KB #72)3.5 Code Cache
Code (reserved=251549KB, committed=14169KB)
(malloc=1949KB #3424)
(mmap: reserved=249600KB, committed=12220KB)3.6 GC
GC (reserved=61771KB, committed=61771KB)
(malloc=17603KB #4501)
(mmap: reserved=44168KB, committed=44168KB)Switching to Serial GC reduces GC native memory to about 1 MB:
$ java -XX:NativeMemoryTracking=summary -Xms300m -Xmx300m -XX:+UseSerialGC -jar app.jar
GC (reserved=1034KB, committed=1034KB)
(malloc=26KB #158)
(mmap: reserved=1008KB, committed=1008KB)3.7 Symbols
Symbol (reserved=10148KB, committed=10148KB)
(malloc=7295KB #66194)
(arena=2853KB #1)3.8 Tracking Over Time
Set a baseline:
$ jcmd <pid> VM.native_memory baseline
Baseline succeededLater compare with:
$ jcmd <pid> VM.native_memory summary.diffThe diff shows how reserved and committed memory changed.
3.9 Detailed NMT
For a fully detailed map, enable -XX:NativeMemoryTracking=detail.
4. Conclusion
This article enumerated the various native memory consumers inside the JVM and demonstrated how to inspect a running application with Native Memory Tracking, enabling more effective tuning of both the application and its runtime environment.
Source: http://www.spring4all.com/article/15116
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
