JVM 进程如何分配其内存?

Posted

技术标签:

【中文标题】JVM 进程如何分配其内存?【英文标题】:How does a JVM process allocate its memory? 【发布时间】:2016-05-31 20:26:23 【问题描述】:

我在理解 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,committed Heap size是374M

元空间大小为 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+=$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 :(

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

【问题讨论】:

JVM本身的所有共享库都有。尝试运行两个 JVM,看看增加了多少。 为出色的研究+1! 100% 同意@PeterLawrey。通过执行ldd /path/to/java' and 'pmap PID 来检查 Java 正在运行哪些共享库,以详细了解 Java 到底使用了什么。 另外,正如你所看到的,“used”比实际的堆大小要小得多。 VM 喜欢占用大量内存来提高 GC 性能。即使是使用过的堆,也有很多可能是死对象,在执行完整 GC 时会被回收。 + CodeCache(用于动态生成的代码)+ GC 内部结构(如 CardTables)+ 本地库分配的结构(I/O、网络等)+ JVM 和库的 .bss 段 【参考方案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 找到。

通过不带任何参数运行jcmd,您可以获得正在运行的Java 应用程序的列表。

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

输出

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

总计: reserved=664192KB,committed=253120KB

Java 堆(保留=516096KB,提交=204800KB)

(mmap:保留=516096KB,提交=204800KB)

(保留=6568KB,提交=4140KB)

(classes #665)

(malloc=424KB, #1000)

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

线程(保留=6868KB,提交=6868KB) (线程 #15)

(stack: reserved=6780KB, committed=6780KB)

(malloc=27KB, #66)

(arena=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 使用的不同内存区域的详细概述,并且还显示了 reservedcommited 内存。

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

进一步阅读:

您还可以将-XX:NativeMemoryTracking=detail 与更多jcmd 命令结合使用。更详细的解释可以在Java Platform, Standard Edition Troubleshooting Guide - 2.6 The jcmd Utility 找到。您可以通过"jcmd &lt;pid&gt; help"查看可能的命令

【讨论】:

您在jcmp 中有错字? 谢谢,稍后会解决这个问题。 感谢您的回答!我尝试使用 NMT 并发现它非常有帮助......但我仍然有一个问题 :) NMT 说我丢失的所有内存都属于“Unkonwn”部分。当我尝试分析详细信息时,我发现以下内容:gist.github.com/krestjaninoff/a89ee990d94d8fc2917a您对这段记忆发生了什么有任何想法?它被 CMS 消耗了吗? “此版本中的 NMT 不跟踪第三方本机代码内存分配和 JDK 类库。” (c) 好的,问题结束了:)

以上是关于JVM 进程如何分配其内存?的主要内容,如果未能解决你的问题,请参考以下文章

限制容器的内存使用量与限制容器内JVM进程的内存使用量?哪个更好?

Java进程内存泄漏判断及解决方法

理解 JVM 内存分配和 Java 内存不足:堆空间

应用重启元凶之JVM内存分配与默认值

java程序运行时内存分配详解

java程序运行时内存分配详解