Java堆外内存之七:JVM NativeMemoryTracking 分析堆外内存泄露
Native Memory Tracking (NMT) 是Hotspot VM用来分析VM内部内存使用情况的一个功能。我们可以利用jcmd(jdk自带)这个工具来访问NMT的数据。
NMT介绍
工欲善其事必先利其器,我们先把相关需要的配置和工具介绍清楚,再通过例子来看看具体如何使用NMT。
打开NMT
NMT必须先通过VM启动参数中打开,不过要注意的是,打开NMT会带来5%-10%的性能损耗。
-XX:NativeMemoryTracking=[off | summary | detail]
# off: 默认关闭
# summary: 只统计各个分类的内存使用情况.
# detail: Collect memory usage by individual call sites.
jcmd查看NMT报告
通过jcmd查看NMT报告以及查看对比情况。
jcmd <pid> VM.native_memory [summary | detail | baseline | summary.diff | detail.diff | shutdown] [scale= KB | MB | GB]
# summary: 分类内存使用情况.
# detail: 详细内存使用情况,除了summary信息之外还包含了虚拟内存使用情况。
# baseline: 创建内存使用快照,方便和后面做对比
# summary.diff: 和上一次baseline的summary对比
# detail.diff: 和上一次baseline的detail对比
# shutdown: 关闭NMT
VM退出时打印NMT
可以通过下面VM参数在JVM退出时打印NMT报告。
-XX:+UnlockDiagnosticVMOptions -XX:+PrintNMTStatistics
NMT实战
症状
某个服务(C)在客户环境使用后发现其内存占用不断变大且远超Xmx指定的大小,导致整个系统因缺少内存造成其他服务无法启动。当时查看到其RSS大约为11G,-Xmx=6G而且heap利用率不到50%。
user@hostxxx> prstat -p 2780
PID USERNAME SIZE RSS STATE PRI NICE TIME CPU PROCESS/NLWP
2780 user 11G 11G sleep 59 0 44:16:39 0.0% java/196
user@hostxxx> /opt/jdk1.8.0_40/bin/jstat -gcutil 2780
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.00 100.00 90.60 46.80 98.02 97.10 11323 4049.745 11 225.345 4275.090
分析
服务通过-Xmx=6G指定最大堆分配为6G,但实际RSS已达到11G,开始怀疑堆外内存是否有内存泄露。为了有更好详细的数据,就在本地重现这个问题,并且打开了NMT持续监控。
NMT的Report如下,重点关注每个分类下的commit大小,这个是实际使用的内存大小。
6739: #进程ID
Native Memory Tracking:
Total: reserved=8491110KB, committed=7220750KB
- Java Heap (reserved=6293504KB, committed=6291456KB)
(mmap: reserved=6293504KB, committed=6291456KB)
- Class (reserved=1107429KB, committed=66189KB)
(classes #11979)
(malloc=1509KB #18708)
(mmap: reserved=1105920KB, committed=64680KB)
- Thread (reserved=159383KB, committed=159383KB)
(thread #156)
(stack: reserved=158720KB, committed=158720KB)
(malloc=482KB #788)
(arena=182KB #310)
- Code (reserved=255862KB, committed=41078KB)
(malloc=6262KB #9319)
(mmap: reserved=249600KB, committed=34816KB)
- GC (reserved=449225KB, committed=449225KB)
(malloc=166601KB #1714646)
(mmap: reserved=282624KB, committed=282624KB)
- Compiler (reserved=395KB, committed=395KB)
(malloc=265KB #856)
(arena=131KB #3)
- Internal (reserved=146041KB, committed=146041KB)
(malloc=132185KB #276370)
(mmap: reserved=13856KB, committed=13856KB)
- Symbol (reserved=31487KB, committed=31487KB)
(malloc=29209KB #91080)
(arena=2278KB #1)
- Native Memory Tracking (reserved=33212KB, committed=33212KB)
(malloc=168KB #2575)
(tracking overhead=33044KB)
- Arena Chunk (reserved=2284KB, committed=2284KB)
(malloc=2284KB)
- Unknown (reserved=12288KB, committed=0KB)
(mmap: reserved=12288KB, committed=0KB)
Virtual memory map:
......
并且在服务器上通过cron job来定期抓取NMT的report保存下来做分析,而且同时也把其对应的RSS和PMAP都抓取了一份。
COLLECTOR_PID=`ps -ef|grep "ProcessName" | grep -v grep | awk '{print $2}'`
OUTDIR=/opt/chkmem
HOSTNAME=`hostname`
prstat -s rss 1 1 > ${OUTDIR}/${HOSTNAME}_coll_${COLLECTOR_PID}_prstat_`date '+%Y%m%d_%H%M%S'`.txt
/opt/jdk1.8.0_40/bin/jcmd ${COLLECTOR_PID} VM.native_memory detail > ${OUTDIR}/${HOSTNAME}_coll_${COLLECTOR_PID}_nmd_`date '+%Y%m%d_%H%M%S'`.txt
pmap -x ${COLLECTOR_PID} > ${OUTDIR}/${HOSTNAME}_coll_${COLLECTOR_PID}_pmap_`date '+%Y%m%d_%H%M%S'`.txt
分析发现NMT中的Symbol域持续增大,从最开始的几十兆已经增加到了2G左右,而且整个jvm的内存使用量也在持续增加。见下图: 
验证后发现问题和JDK8的一个bug https://bugs.java.com/view_bug.do?bug_id=8180048 非常类似,测试后也证实确实如此,最后通过升级JDK解决了这个问题。具体是那个组件命中了JDK的这个bug,会在下一篇文章中详细描述。
Java堆外内存之七:JVM NativeMemoryTracking 分析堆外内存泄露的更多相关文章
- [置顶] linux内核启动2-setup_arch中的内存初始化(目前分析高端内存)
上一篇微博留下了这几个函数,现在我们来分析它们 sanity_check_meminfo(); arm_memblock_init(&meminfo, mdes ...
- OutOfMemoryError/OOM/内存溢出异常实例分析--堆内存溢出
Java堆内存溢出 只要不断创建对象,并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象, 那么在对象数量到达最大堆的容量限制后就会产生内存溢出异常,代码如下: import ...
- JVM内存结构/JVM运行时数据区,以及堆内存的划分
1.程序计数器: 程序计数器是线程私有的内存,JVM多线程是通过线程轮流切换并分配处理器执行时间的方式实现的,当线程切换后需要恢复到正确的执 行位置(处理器)时,就是通过程序计数器来实现的.此内存区域 ...
- Java内存管理:Java内存区域 JVM运行时数据区
转自:https://blog.csdn.net/tjiyu/article/details/53915869 下面我们详细了解Java内存区域:先说明JVM规范定义的JVM运行时分配的数据区有哪些, ...
- JVM基础(1)——内存模型
转载:http://blog.csdn.net/weitry/article/details/53264262 系列文章规划: JVM基础(1)——内存模型 JVM基础(2)——内存管理 JVM基础( ...
- OutOfMemoryError/OOM/内存溢出异常实例分析--虚拟机栈和本地方法栈溢出
关于虚拟机栈和本地方法栈,在JVM规范中描述了两种异常: 1.如果线程请求的栈深度大于JVM所允许的深度,将抛出StackOverflowError异常: 2.如果虚拟机在扩展栈时无法申请到足够的内存 ...
- JVM初探- 使用堆外内存减少Full GC
JVM初探-使用堆外内存减少Full GC 标签 : JVM 问题: 大部分主流互联网企业线上Server JVM选用了CMS收集器(如Taobao.LinkedIn.Vdian), 虽然CMS可与用 ...
- java中栈内存与堆内存(JVM内存模型)
java中栈内存与堆内存(JVM内存模型) Java中堆内存和栈内存详解1 和 Java中堆内存和栈内存详解2 都粗略讲解了栈内存和堆内存的区别,以及代码中哪些变量存储在堆中.哪些存储在栈中.内存中的 ...
- 深入了解java虚拟机(JVM) 第三章 内存区域----堆空间
一.堆的含义 jvm堆的区域主要是用来存放对象的实例,它的空间大小是JVM内存区域中占比重最大的,也是jvm最大的内存管理模块,最重要的是,这个区域是垃圾收集器主要管理的区域,这意味着我们在考虑垃圾回 ...
随机推荐
- uname命令行
常用命令uname -v # uname -i #uname -a dream361@master:~$ uname -n #主机名称 master dream361@master:~$ uname ...
- python爬虫之requests模块
一. 登录事例 a. 查找汽车之家新闻 标题 链接 图片写入本地 import requests from bs4 import BeautifulSoup import uuid response ...
- 自定义控件-滑动条SeekBar
一.效果图 二.实现思路 1.控件继承自View 2.重写两个方法onDraw() 绘制页面和 onTouch() 添加监听 3.onDraw(Canvas ca)中 a.new Canvas ...
- APUE学习笔记——10.9 信号发送函数kill、 raise、alarm、pause
转载注明出处:Windeal学习笔记 kil和raise kill()用来向进程或进程组发送信号 raise()用来向自身进程发送信号. #include <signal.h> int k ...
- 一个好工具-everything-可以找到浏览器的所有缓存
下载路径http://www.voidtools.com/downloads/ 我用它来寻找浏览器缓存的google瓦片.
- iOS数组的去重,判空,删除元素,删除重复元素 model排序 等
一: 去重 有时需要将NSArray中去除重复的元素,而存在NSArray中的元素不一定都是NSString类型.今天想了想,加上朋友的帮助,想到两种解决办法,先分述如下. 1.利用NSDiction ...
- [IC]Lithograph(0)半导体制造的基本过程
1. 晶圆片 Wafer 晶圆(Wafer)是指硅半导体集成电路制作所用的硅芯片,由于其形状为圆形,故称为晶圆.晶圆是生产集成电路所用的载体,一般意义晶圆多指单晶硅圆片. 半导体行业从图1到图2,是一 ...
- http_server实例代码
from socket import * def handleClient(connfd): print("-----") request = connfd.recv(4096) ...
- TreeSet中自定义Comparator实现降序
@Test public void test1() { TreeSet ts = new TreeSet<Integer>(new MyComparator()); ts.add(3); ...
- BZOJ4903 UOJ300 CTSC2017 吉夫特 【Lucas定理】
BZOJ4903 UOJ300 CTSC2017 吉夫特 弱弱地放上题目链接 Lucas定理可以推一推,发现C(n,m)是奇数的条件是n" role="presentation&q ...