参考文章

linux ps top 命令

JAVA堆外内存排查小结 - 知乎

记一次堆外内存泄漏排查过程 | HeapDump性能社区

JVM源码分析之堆外内存完全解读 - 你假笨

JVM源码分析之临门一脚的OutOfMemoryError完全解读 - 你假笨

内存划分

image.png

分析堆外情况

(NMT+jcmd+pmap+gdb dump)

  • NMT(native memory tracking)
    使用:在JVM参数中添加 -XX:NativeMemoryTracking=[off | summary | detail]

    1. -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堆和永久代之外,下面这些区域也会占用较多的内存,这里所有的内存总和会受到操作系统进程最大内存的限制:

  1. Direct Memory:可以通过-XX:MaxDirectMemorySize调整大小,内存不足时抛出OutOfMemoryError或OutOfMemoryError:Direct buffer memory。
  2. 线程堆栈:可通过-Xss调整大小内存不足时抛出StackoverflowError(纵向无法分配,即无法分配新的栈帧)或OutOfMemoryError:unable to create new native thread(横向无法分配,即无法建立新的线程)。
  3. Socket缓存区:每个Socket连接都Receive和Send两个缓存区,分别占大约37KB和25KB的内存,连接多的话这块内存占用也比较可观。如果无法分配,则可能会抛出IOException:Too many open files异常。
  4. JNI代码:如果代码中使用JNI调用本地库,那么本地库使用内存也不在堆中
  5. 虚拟机和GC:虚拟机和GC的代码执行也要消耗一定的内存。