JVM 进程如何分配其内存?

2022-09-03 07:36:41

在理解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

根据 ,承诺的堆大小为 374Mjvisualvm enter image description here

元层大小为89M
enter image description here

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

我的应用(平均)有 36 个实时线程enter image description here

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

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

所以,在这里我们可以添加36M

该应用程序使用DirectBuffer的唯一地方是NIO。据我从JMX中看到的,它没有消耗大量资源 - 只有98K enter image description here

最后一步是映射库和 jar。但根据(满量输出)pmap)

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

12896K

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

9720K

我们这里只有20M

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

有谁知道我错过了什么?


答案 1

方法:

您可能希望使用 Java HotSpot Native Memory Tracking (NMT)。

这可以为您提供由JVM分配的内存的确切列表,这些内存被拆分为堆,类,线程,代码,GC,编译器,内部,符号,内存跟踪,池化可用块未知的不同区域。

用法

  • 您可以使用 启动应用程序。-XX:NativeMemoryTracking=summary

  • 可以使用 来完成当前堆的观察。jcmd <pid> VM.native_memory summary

在哪里可以找到jcmd / pid

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

只需在没有任何参数的情况下运行,即可获得正在运行的 Java 应用程序的列表。jcmd

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

输出

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

总计:保留 = 664192KB,已提交 = 253120KB <---本机内存跟踪跟踪的总内存

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

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

  • (保留 = 6568KB,已提交 = 4140KB)<---类元数据

    (类 #665) <---装入类的数量

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

    (mmap:保留 = 6144KB,已提交 = 3716KB)

  • 线程(保留 = 6868KB,已提交 = 6868KB)(线程 #15)<---线程数

    (堆栈:保留 = 6780KB,提交 = 6780KB)线程堆栈使用的<---内存

    (malloc=27KB, #66)

    (竞技场 = 61KB,#30)<---资源和句柄区域

  • 代码(保留 = 102414KB,已提交 = 6314KB)

    (malloc=2574KB, #74316)

    (mmap:保留 = 99840KB,已提交 = 3740KB)

  • GC (保留 = 26154KB,已提交 = 24938KB)

    (malloc=486KB, #110)

    (mmap: 保留 = 25668KB, 提交 = 24452KB)

  • 编译器(保留 = 106KB,已提交 = 106KB)

    (malloc=7KB, #90)

    (竞技场 = 99KB,#3)

  • 内部(保留 = 586KB,已提交 = 554KB)

    (malloc=554KB, #1677)

    (mmap:保留 = 32KB,已提交 = 0KB)

  • 符号(保留 = 906KB,已提交 = 906KB)

    (malloc=514KB, #2736)

    (竞技场= 392KB,#1)

  • 内存跟踪(保留 = 3184KB,已提交 = 3184KB)

    (malloc=3184KB, #300)

  • 池化可用块(保留 = 1276KB,已提交 = 1276KB)

    (malloc=1276KB)

  • 未知(保留 = 33KB,已提交 = 33KB)

    (竞技场 = 33KB,#1)

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

我不知道有哪种技术可以给你一个更详细的内存消耗列表。

延伸阅读:

您还可以与其他命令结合使用。更详细的解释可以在Java平台,标准版故障诊断指南 - 2.6 jcmd实用程序中找到。您可以通过以下方式检查可能的命令-XX:NativeMemoryTracking=detailjcmd"jcmd <pid> help"


答案 2

推荐