JVM 进程如何分配内存?

How does a JVM process allocate its memory?

我对 JVM 进程如何分配自己的内存的理解有一点差距。据我所知

RSS = Heap size + MetaSpace + OffHeap size

其中 OffHeap 由线程堆栈、直接缓冲区、映射文件(库和 jar)和 JVM 代码本身组成;

目前我正在尝试分析我的 Java 应用程序(Spring Boot + Infinispan),RSS 是 779M(它运行在docker 容器,所以 pid 1 没问题):

[ root@daf5a5ae9bb7:/data ]$ ps -o rss,vsz,sz 1
RSS    VSZ    SZ
798324 6242160 1560540

根据jvisualvm,提交的堆大小为374M

Metasapce 大小为 89M

换句话说,我想解释一下799M - (374M + 89M) = 316M OffHeap内存。

我的应用有(平均)36 个活动线程

这些线程中的每一个消耗 1M:

[ root@fac6d0dfbbb4:/data ]$ java -XX:+PrintFlagsFinal -version |grep ThreadStackSize    
intx CompilerThreadStackSize                   = 0
intx ThreadStackSize                           = 1024
intx VMThreadStackSize                         = 1024

所以,这里我们可以加上36M.

应用程序唯一使用DirectBuffer 的地方是NIO。据我从 JMX 可以看出,它并没有消耗很多资源——只有 98K

最后一步是映射库和罐子。但是根据 pmap (full output)

[ root@daf5a5ae9bb7:/data ]$ pmap -x 1 | grep ".so.*" | awk '{ sum+=} END {print sum}'

12896K

加上

root@daf5a5ae9bb7:/data ]$ pmap -x 1 | grep “.jar" | awk '{ sum+=} END {print sum}'

9720K

这里只有20M

因此,我们还要解释316M - (36M + 20M) = 260M :(

有人知道我错过了什么吗?

方法:

您可能想要使用 Java HotSpot Native Memory Tracking (NMT)

这可能会为您提供 JVM 分配的内存的确切列表,分为不同的区域堆,类,线程,代码,GC,编译器,内部,符号,内存跟踪,池化空闲块,未知

用法:

  • 您可以使用-XX:NativeMemoryTracking=summary开始申请。

  • 可以用jcmd <pid> VM.native_memory summary观察当前堆。

在哪里可以找到 jcmd/pid:

在 Ubuntu 上的默认 OpedJDK 安装上,可以在 /usr/bin/jcmd 找到它。

仅 运行 jcmd 不带任何参数,您将获得 运行 Java 个应用程序的列表。

user@pc:~$ /usr/bin/jcmd
5169 Main                       <-- 5169 is the pid

输出:

然后您将收到堆的完整概览,如下所示:

Total: reserved=664192KB, committed=253120KB <--- total memory tracked by Native Memory Tracking

  • Java Heap (reserved=516096KB, committed=204800KB) <--- Java Heap

    (mmap: reserved=516096KB, committed=204800KB)

  • Class (reserved=6568KB, committed=4140KB) <--- class metadata

    (classes #665) <--- number of loaded classes

    (malloc=424KB, #1000) <--- malloc'd memory, #number of malloc

    (mmap: reserved=6144KB, committed=3716KB)

  • Thread (reserved=6868KB, committed=6868KB) (thread #15) <--- number of threads

    (stack: reserved=6780KB, committed=6780KB) <--- memory used by thread stacks

    (malloc=27KB, #66)

    (arena=61KB, #30) <--- resource and handle areas

  • Code (reserved=102414KB, committed=6314KB)

    (malloc=2574KB, #74316)

    (mmap: reserved=99840KB, committed=3740KB)

  • GC (reserved=26154KB, committed=24938KB)

    (malloc=486KB, #110)

    (mmap: reserved=25668KB, committed=24452KB)

  • Compiler (reserved=106KB, committed=106KB)

    (malloc=7KB, #90)

    (arena=99KB, #3)

  • Internal (reserved=586KB, committed=554KB)

    (malloc=554KB, #1677)

    (mmap: reserved=32KB, committed=0KB)

  • Symbol (reserved=906KB, committed=906KB)

    (malloc=514KB, #2736)

    (arena=392KB, #1)

  • Memory Tracking (reserved=3184KB, committed=3184KB)

    (malloc=3184KB, #300)

  • Pooled Free Chunks (reserved=1276KB, committed=1276KB)

    (malloc=1276KB)

  • Unknown (reserved=33KB, committed=33KB)

    (arena=33KB, #1)

这详细概述了 JVM 使用的不同内存区域,还显示了保留提交 内存。

我不知道有什么技术可以为您提供更详细的内存消耗列表。

进一步阅读:

您还可以将 -XX:NativeMemoryTracking=detail 与其他 jcmd 命令结合使用。可以在 Java Platform, Standard Edition Troubleshooting Guide - 2.6 The jcmd Utility 找到更详细的解释。您可以通过 "jcmd <pid> help"

检查可能的命令