参考文章
JVM源码分析之临门一脚的OutOfMemoryError完全解读 - 你假笨
内存划分
分析堆外情况
(NMT+jcmd+pmap+gdb dump)
NMT(native memory tracking)
使用:在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
NMT报告会显示内存使用情况
类别 含义
Java Heap 堆大小
Thread 线程
Thread Stack 线程栈
NMT可以得到线程栈大小,排除栈空间影响。
pmap 查看进程内存地址空间
pmap -x pid | sort xx
可以结合pmap,和nmt得到内存地址空间和堆外占用情况了。
接下来需要做的就是分析堆外内存的内容了。gdb dump查看内存空间内容
(gdb) dump binary memory ./file BEGIN_ADDRESS END_ADDRESS
将内存内容dump到文件中,就可以查看到文件中的内容了。
但是这种方式不直观,所以可以使用其他工具:gperf
:google的,使用gperf2.5即可,老版本要安装libunwind,2.5的版本不需要了。
https://blog.csdn.net/unix21/article/details/79161250
另外一个注意点就是虽然heap文件只有1M,但是可以分析出堆外内存的大小。总结
从实践经验来看,除了java堆和永久代之外,下面这些区域也会占用较多的内存,这里所有的内存总和会受到操作系统进程最大内存的限制:
- Direct Memory:可以通过
-XX:MaxDirectMemorySize
调整大小,内存不足时抛出OutOfMemoryError或OutOfMemoryError:Direct buffer memory。 - 线程堆栈:可通过-Xss调整大小内存不足时抛出StackoverflowError(纵向无法分配,即无法分配新的栈帧)或OutOfMemoryError:unable to create new native thread(横向无法分配,即无法建立新的线程)。
- Socket缓存区:每个Socket连接都Receive和Send两个缓存区,分别占大约37KB和25KB的内存,连接多的话这块内存占用也比较可观。如果无法分配,则可能会抛出IOException:Too many open files异常。
- JNI代码:如果代码中使用JNI调用本地库,那么本地库使用内存也不在堆中
- 虚拟机和GC:虚拟机和GC的代码执行也要消耗一定的内存。