通常情況下, JVM占用的內存不僅僅是-Xmx, -Xms等指定的大小, 因為JVM也是一個應用, 它需要額外的空間去完成它的工作, 除了堆外, JVM會分配內存的地方包括以下這些:
Metaspace: 元數據區, 存儲類, 及方法的元數據信息
Threads: 線程, 線程里的棧還是比較耗內存的, 在64位操作系統上, 默認棧的大小為1MB, 當然可以通過-Xss配置。
因為一般情況下線程的數量是沒有限制的, 因此可能會占用極其多的內存。
Code Cache: JVM通過JIT把字節碼轉換成機器指令, 然后把這些指令放到一個非堆區域Code Cache。
配置參數:
-XX:InitialCodeCacheSize
: 初始大小
-XX:ReservedCodeCacheSize
: 最大的空間大小
Garbage Collection: JVM的垃圾回收器需要使用到一個空間去完成它們的任務, 一些運行時的數據等等, 這個空間使用的更多的是本地內存(native memory)
符號表:
String Pool: JVM復用字面量字符串的地方。
配置參數:
-XX:StringTableSize
: 常量池大小
運行時常量池,JVM用來存儲編譯時的字面量或方法以及屬性地址(reference)。
Native Byte Buffers:
開發者可以直接使用本地內存, 如通過JNI的malloc
或者NIO的ByteBuffers
等。
Native Memory Tracking
那么怎么監控JVM使用到的這些內存嗎, 答案就是通過NMT(Native Memory Tracking), 但是使用它之前需要設置JVM的啟動參數:
-XX:NativeMemoryTracking
, 可能的值為off, 關閉,也為默認值, summary, 顯示匯總信息, detail, 顯示詳細信息。
使用方法:
首先通過jps
找到對應的Java程序的pid,然后使用如下命令:
jcmd <pid> VM.native_memory
我在本地運行jcmd后輸出結果為:
11132:
Native Memory Tracking:
Total: reserved=3517807KB, committed=548183KB
- Java Heap (reserved=2045952KB, committed=366080KB)
(mmap: reserved=2045952KB, committed=366080KB)
- Class (reserved=1089619KB, committed=46803KB)
(classes #8341)
(malloc=6227KB #11407)
(mmap: reserved=1083392KB, committed=40576KB)
- Thread (reserved=29820KB, committed=29820KB)
(thread #30)
(stack: reserved=29696KB, committed=29696KB)
(malloc=89KB #152)
(arena=35KB #59)
- Code (reserved=251467KB, committed=10383KB)
(malloc=1867KB #4673)
(mmap: reserved=249600KB, committed=8516KB)
- GC (reserved=80655KB, committed=74803KB)
(malloc=5775KB #218)
(mmap: reserved=74880KB, committed=69028KB)
- Compiler (reserved=149KB, committed=149KB)
(malloc=18KB #478)
(arena=131KB #3)
- Internal (reserved=6719KB, committed=6719KB)
(malloc=6655KB #11664)
(mmap: reserved=64KB, committed=64KB)
- Symbol (reserved=11371KB, committed=11371KB)
(malloc=9477KB #85817)
(arena=1894KB #1)
- Native Memory Tracking (reserved=1880KB, committed=1880KB)
(malloc=72KB #1130)
(tracking overhead=1807KB)
- Arena Chunk (reserved=176KB, committed=176KB)
(malloc=176KB)
11132是進程號pid
然后出現的很多reserved是指總共可用的內存大小, 而commited是指已經使用的內存大小。
追蹤本地內存的變化
NMT可以讓我們看到隨時間增長本地內存的變化。
首先需要設置一個對照的內存狀態:
jcmd <pid> VM.native_memory baseline
然后過一段時間如果需要查看變化狀態即:
jcmd <pid> VM.native_memory detail.diff
NMT通過+和-來顯示內存的變化:
Total: reserved=1771487KB +3373KB, committed=491491KB +6873KB
- Java Heap (reserved=307200KB, committed=307200KB)
(mmap: reserved=307200KB, committed=307200KB)
- Class (reserved=1084300KB +2103KB, committed=39356KB +2871KB)
Refer: https://www.baeldung.com/native-memory-tracking-in-jvm