这篇文章将整理一下java 堆外内存的问题的一些常用处理方法。
java 内存占用盘点 java 实际会使用内存情况如下
堆内 
线程栈 
方法区 
二进制代码 
堆外 
 
 
xmx 
线程数 x xss 
存储类定义以及常量池 
jit多层编译代码 
被netty或其他堆外缓存使用 
 
 
注意: 只有除了堆内,其他内存都占用的堆外内存, 他们都算整个java进程占用的内存,但无法被类似 jmap,mat 等堆内分析工具分析。
使用NMT 追踪 JVM 自用的堆外内存情况 NMT是Java7U40引入的HotSpot新特性,可用于监控JVM原生内存的使用,但比较可惜的是,目前的NMT不能监控到JVM之外或原生库分配的内存
使用方式,需要在jvm 启动时加上参数, (注意: 据官方文档 开启该功能会影响 5%-10% 的性能)
配置方式:
1 -XX:NativeMemoryTracking=summary 或者 -XX:NativeMemoryTracking=detail 
查询方式: 
1 jcmd <pid> VM.native_memory [summary | detail | baseline | summary.diff | detail.diff 
e.g
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 jcmd 35740 VM.native_memory summary 35740: Native Memory Tracking: Total: reserved=3516019KB, committed=244959KB                   堆内 -                 Java Heap (reserved=2097152KB, committed=131072KB)                             (mmap: reserved=2097152KB, committed=131072KB)                       类数据 -                     Class (reserved=1062038KB, committed=10518KB)                             (classes #873)                             (malloc=5270KB #462)                             (mmap: reserved=1056768KB, committed=5248KB)                      线程栈所占空间 -                    Thread (reserved=16453KB, committed=16453KB)                             (thread #17)                             (stack: reserved=16384KB, committed=16384KB)                             (malloc=49KB #86)                             (arena=20KB #33)                                                生成代码 -                      Code (reserved=249803KB, committed=2739KB)                             (malloc=203KB #651)                             (mmap: reserved=249600KB, committed=2536KB)                                                    gc 占用的空间 比如 card table -                        GC (reserved=82395KB, committed=75999KB)                             (malloc=5771KB #121)                             (mmap: reserved=76624KB, committed=70228KB)                    编译生成代码时所占用的空间 -                  Compiler (reserved=138KB, committed=138KB)                             (malloc=7KB #41)                             (arena=131KB #3)                    其他内部内存 command line parser, JVMTI, properties and so on -                  Internal (reserved=5787KB, committed=5787KB)                             (malloc=5755KB #2844)                             (mmap: reserved=32KB, committed=32KB)                      Symbol:保留字符串(Interned String)的引用与符号表引用放在这里        -                    Symbol (reserved=1962KB, committed=1962KB)                             (malloc=1250KB #2173)                             (arena=712KB #1)      本NMT 工具自己所需内存 -    Native Memory Tracking (reserved=105KB, committed=105KB)                             (malloc=3KB #41)                             (tracking overhead=101KB)                 文档未提及 -               Arena Chunk (reserved=187KB, committed=187KB)                             (malloc=187KB) 
各项解释的官方文档 
保留内存(reserved):reserved memory 是指JVM 通过mmaped PROT_NONE 申请的虚拟地址空间,在页表中已经存在了记录(entries),保证了其他进程不会被占用,且保证了逻辑地址的连续性,能简化指针运算。
提交内存(commited):committed memory 是JVM向操做系统实际分配的内存(malloc/mmap),mmaped PROT_READ | PROT_WRITE,仍然会page faults,但是跟 reserved 不同,完全内核处理像什么也没发生一样。
其他内部内存 (Internal): 注意 Netty 使用的 ByteBuffer.allocateDirect 内部调用的 Unsafe.allocateMemory 内部其实会被 NMT 收集为 Internal 分类。
但是 MappedByteBuffers( 由 FileChannel.map 触发) 则不会被 NMT 收集到。但却会被操作系统收集。
所以,对于这个堆外内存的监控,jcmd 还不够,得上后续linux 的通用进程 内存分析工具。
这里需要注意的是:由于malloc/mmap的lazy allocation and paging机制,即使是commited的内存,也不一定会真正分配物理内存。
自用堆外内存如何追踪 使用pmap 工具查看进程内存占用 pmap -x {pid}
e.g
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 [root@test-b12-java-3-52 ~]# pmap -x 140087 140087:   /usr/j2sdk/jdk1.8.0_191/bin/java -Djava.util.logging.config.file=/root/apache-tomcat-xxx Address           Kbytes     RSS   Dirty Mode   Mapping 0000000000400000       4       0       0 r-x--  java 0000000000600000       4       4       4 r----  java 0000000000601000       4       4       4 rw---  java 00000000007d9000     580     508     508 rw---    [ anon ] 0000000080000000 2105236 2105044 2105044 rw---    [ anon ] ... 00007fc21b3aa000       4       4       4 rw---    [ anon ] 00007ffd9291e000      84      44      44 rw---    [ stack ] 00007ffd92972000       4       4       0 r-x--    [ anon ] ffffffffff600000       4       0       0 r-x--    [ anon ] ----------------  ------  ------  ------ total kB        16462260 3103136 3096156 
可见上述进程 实际占用内存 rss 大约3G
pmap命令内部实际是读取了/proc/pid/maps和/porc/pid/smaps文件来输出。
使用strace 监控下内存分配和回收的系统调用 strace -o xxx.txt -T -tt -e mmap,munmap,mprotect -fp 12
e.g
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 140140 23:00:00.624788 mprotect(0x7fc21b3a5000, 4096, PROT_READ) = 0 <0.000205> 140140 23:00:00.625226 mprotect(0x7fc21b3a5000, 4096, PROT_READ|PROT_WRITE) = 0 <0.000168> 140140 23:00:00.625604 mprotect(0x7fc21b3a6000, 4096, PROT_NONE) = 0 <0.000189> 140140 23:00:00.688019 mprotect(0x7fc21b3a6000, 4096, PROT_READ) = 0 <0.000161> 140140 23:00:12.897296 mprotect(0x7fc21b3a5000, 4096, PROT_READ) = 0 <0.000374> 142593 23:00:12.898044 --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_ACCERR, si_addr=0x7fc21b3a5880} --- 20726 23:00:12.898063 --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_ACCERR, si_addr=0x7fc21b3a5480} --- 140140 23:00:12.898075 mprotect(0x7fc21b3a5000, 4096, PROT_READ|PROT_WRITE) = 0 <0.000266> 140140 23:00:12.899591 mprotect(0x7fc21b3a6000, 4096, PROT_NONE) = 0 <0.000250> 140140 23:00:12.905392 mprotect(0x7fc21b3a6000, 4096, PROT_READ) = 0 <0.000115> 140140 23:00:13.101242 mprotect(0x7fc21b3a5000, 4096, PROT_READ) = 0 <0.000272> 140140 23:00:13.101815 mprotect(0x7fc21b3a5000, 4096, PROT_READ|PROT_WRITE) = 0 <0.000235> 140140 23:00:13.102247 mprotect(0x7fc21b3a6000, 4096, PROT_NONE) = 0 <0.000149> 140140 23:00:13.158721 mprotect(0x7fc21b3a6000, 4096, PROT_READ) = 0 <0.000213> 142592 23:00:13.177198 mmap(NULL, 44793, PROT_READ, MAP_SHARED, 96, 0x84000) = 0x7fc1fc068000 <0.000400> 142592 23:00:13.181533 munmap(0x7fc1fc068000, 44793) = 0 <0.000445> 142592 23:00:13.246486 mmap(NULL, 44793, PROT_READ, MAP_SHARED, 96, 0x84000) = 0x7fc1fc068000 <0.000381> 
参考 https://blog.csdn.net/qianshangding0708/article/details/100978730 
https://stackoverflow.com/questions/47309859/what-is-contained-in-code-internal-sections-of-jcmd 
https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/tooldescr007.html 
https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/tooldescr022.html#BABHIFJC 
http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/a9b412abe617/src/share/vm/memory/allocation.hpp#l147 
https://www.baeldung.com/java-stack-heap