JVM 堆内存一般分析的比较多,本篇谈谈堆外内存问题排查,通常我们需要排查堆外内存的原因是系统整个内存使用飙高,但是堆内内存使用正常。这时候就需要分析堆外内存了
通常JVM的参数我们会配置
-Xms 堆初始内存 -Xmx 堆最大内存 -XX:+UseG1GC/CMS 垃圾回收器 -XX:+DisableExplicitGC 禁止显示GC -XX:MaxDirectMemorySize 设置最大堆外内存,默认是-xmx-survivor,也就是基本上和-xmx大小相等 -Xss:每个线程的堆栈大小,默认1M -Xmn: 年轻代大小(eden区+2 survivor) -XX:newRatio: 4 年轻代与老年代1:4 -XX:survivorRatio: 8Eden区与survivor大小比值
java整个进程占用的内存:
堆外内存回收: 堆外内存的回收是通过system.gc()来的,依赖于目前的gc机制。 通常是通过DirectByteBuffer对象来分配堆外内存,gc的时候就是判断这个对象是否被引用,来决定是否回收。
jstat -gc pid 1000
S0C/S0U | S1C/S1U | EC/EU | CCSC/CCSU | YGC/YGCT | FGC/FCGT | GCT |
---|---|---|---|---|---|---|
survivor0容量和使用 | survivor1容量和使用 | Eden | jdk8是meta,以前应是PC,PU | young gc次数和耗时 | full gc次数和耗时 | total gc时间 |
排除掉heap的问题
使用 在JVM参数中添加 -XX:NativeMemoryTracking=[off | summary | detail]
-XX:NativeMemoryTracking=detail
在JVM运行过程中,使用jcmd获取相关信息 jcmd pid VM.native_memory [summary | detail | baseline | summary.diff | detail.diff | shutdown] [scale= KB | MB | GB]
jcmd pid VM.native_memory detail
baseline个基准,之后会输出diff参数,来和这个基线版本进行比较,可以两次的内存差 NMT报告会显示内存使用情况
类别 | 含义 |
---|---|
Java Heap | 堆大小 |
Thread | 线程 |
Thread Stack | 线程栈 |
更多参考:
https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/tooldescr022.html
NMT可以得到线程栈大小,排除栈空间影响
pmap -x pid | sort xx
可以结合pmap,和nmt得到内存地址空间。和堆外占用情况了
接下来需要做的就是分析堆外内存的内容了。
(gdb) dump binary memory ./file BEGIN_ADDRESS END_ADDRESS
将内存内容dump到文件中,就可以查看到文件中的内容了。 但是这种方式不直观,所以可以使用其他工具
export LD_PRELOAD=/usr/local/lib/libjemalloc.so
export MALLOC_CONF=prof:true,lg_prof_interval:31,lg_prof_sample:17,prof_prefix:/output/jeprof
https://github.com/jemalloc/jemalloc/wiki/Getting-Started
最后分析是dubbo,rpc调用过程中,有很多的数据传输对象,而堆外内存大小又没有限制,导致内存持续飙高
https://www.cnblogs.com/softidea/p/5267757.html https://blog.csdn.net/u014459326/article/details/53609885 https://www.cnblogs.com/redcreen/archive/2011/05/04/2037057.html http://lovestblog.cn/blog/2015/05/07/system-gc/ https://blog.csdn.net/kringpin_lin/article/details/26211119 https://blog.csdn.net/jicahoo/article/details/50933469 http://tinylab.org/the-builtin-heap-profiling-of-jemalloc/