背景:
在阿里云一个ECS机器上(4核16G)部署一个应用;运行时查看进程占用内存为12g!
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
18650 root 30 10 17.699g 0.012t 19452 S 7.3 78.8 13:04.42 java
jvm启动参数:
GC_PARAM="-Xmx10g -Xms10g -XX:G1HeapRegionSize=2m"
问题:为什么是12g?最大堆内存才10g!
首先要理解jvm内存模型():
JVM进程内存 ≈ JVM程序自身占用内存+Java永久代 + Java堆(新生代和老年代) + 线程栈+ Java NIO(直接内存)
第一部分:JVM程序自身占用内存 ;理论上不大 ;字节码
第二部分:可以通过jmap -heap 18431来查看新生代、老年代和永久代的内存情况; 10G
G1 GC介绍
Attaching to process ID 18431, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.144-b01
using thread-local object allocation.
Garbage-First (G1) GC with 8 thread(s)
Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize = 10737418240 (10240.0MB)
NewSize = 1363144 (1.2999954223632812MB)
MaxNewSize = 6442450944 (6144.0MB)
OldSize = 5452592 (5.1999969482421875MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 21807104 (20.796875MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 17592186044415 MB
G1HeapRegionSize = 2097152 (2.0MB)
Heap Usage:
G1 Heap:
regions = 5120
capacity = 10737418240 (10240.0MB)
used = 8176447264 (7797.667755126953MB)
free = 2560970976 (2442.332244873047MB)
76.14909917116165% used
G1 Young Generation:
Eden Space:
regions = 2074
capacity = 6249512960 (5960.0MB)
used = 4349493248 (4148.0MB)
free = 1900019712 (1812.0MB)
69.59731543624162% used
Survivor Space:
regions = 63
capacity = 132120576 (126.0MB)
used = 132120576 (126.0MB)
free = 0 (0.0MB)
100.0% used
G1 Old Generation:
regions = 1801
capacity = 4355784704 (4154.0MB)
used = 3692736288 (3521.667755126953MB)
free = 663048416 (632.3322448730469MB)
84.77775048451981% used
第三部分:线程栈计算如下:
(1)在linux环境中可以通过ps -xH|grep {pid}|wc -l来统计该应用的线程数,线程数*Xss=线程栈总内存
(1.0)ps -xH|grep 18431|wc -l = 1015 个应用线程;
(2)使用ulimit -s可以查看线程栈最大大小:8192 = 8M;
(2.0)1015*(1-8M)=(1-8)g
第四部分:Java NIO(直接内存)
NIO直接内存可以通过netstat -np|grep {pid}|wc -l统计出当前nio数,再乘以每个nio所分配ByteBuffer大小(读写缓冲区)
116 * (ByteBuffer大小(假设1M-10M))=(116M-1g)
综上 12g 的由来 = 线程栈 + nio直接内存 + heap + 程序