在Docker容器中运行的JVM的驻留集大小(RSS)和Java总提交内存(NMT)之间的差异

fiei3ece  于 2022-11-07  发布在  Docker
关注(0)|答案(2)|浏览(479)

方案:

我有一个在Docker容器中运行的JVM。我使用两个工具做了一些内存分析:1)top2)Java本机内存跟踪。这些数字看起来很混乱,我正在试图找到导致差异的原因。

问题:

RSS被报告为1272 MB用于Java进程,而总Java内存被报告为790.55 MB。我该如何解释剩余内存1272 - 790.55 = 481.44 MB去了哪里?

为什么在查看了SO上的this question后仍要保留此问题:

我确实看到了答案,并且解释得很有道理。但是,在获得Java NMT和pmap -x的输出后,我仍然无法具体Map哪些Java内存地址是实际驻留的和物理Map的。我需要一些具体的解释(详细的步骤)来找出RSS和Java总提交内存之间的差异的原因。

顶部输出

Java不超过

Docker内存统计数据

图表

我有一个运行了超过48小时的码头集装箱。现在,当我看到一个图表,其中包含:
1.分配给Docker容器的总内存= 2 GB

  1. Java最大堆= 1 GB
    1.提交总量(JVM)=始终小于800 MB
    1.已用堆(JVM)=始终小于200 MB
    1.未使用的堆(JVM)=始终小于100 MB。
  2. RSS =约1.1 GB。
    那么,是什么在吞噬1.1 GB(RSS)和800 MB(Java总提交内存)之间的内存?

1aaf6o9v

1aaf6o9v1#

Mikhail Krestjaninoff的“Analyzing java memory usage in a Docker container“中,您可以找到一些线索:
(And需要说明的是,三年后的2019年5月,thesituation does improveswith openJDK 8u212

ResidentSetSize指当前分配给一个处理并由其使用的物理内存数量(不包括换出的页面)。它包括代码、数据和共享库(计算在每个使用它们的处理中)

为什么docker统计信息与ps数据不同?
第一个问题的答案很简单-Docker has a bug (or a feature - depends on your mood):它将文件缓存包括在总内存使用信息中。因此,我们可以避免使用这个指标,而使用有关RSS的ps信息。
好吧,好吧-但是为什么RSS比Xmx高?
理论上,在java应用程序的情况下

RSS = Heap size + MetaSpace + OffHeap size

其中OffHeap由线程堆栈、直接缓冲区、Map文件(库和jar)和JVM代码本身组成
自从JDK 1.8.40以来,我们有**Native Memory Tracker**!
正如您所看到的,我已经将-XX:NativeMemoryTracking=summary属性添加到JVM中,因此我们可以从命令行调用它:

docker exec my-app jcmd 1 VM.native_memory summary

(This是OP所做的)
不要担心“未知”部分-似乎NMT是一个不成熟的工具,不能处理CMS GC(当您使用另一个GC时,该部分消失)。
请记住,NMT显示的是“committed”内存,而不是“resident”内存(通过ps命令获得)。换句话说,可以提交内存页而不将其视为驻留内存(直到直接访问它为止)

这意味着非堆区域的NMT结果(堆始终是预初始化的)可能大于RSS值

(that是“Why does a JVM report more committed memory than the linux process resident set size?“的用武之地)
因此,尽管我们将jvm堆限制设置为256 M,但我们的应用程序消耗了367 M。“其他”的164 M主要用于存储类元数据、编译代码、线程和GC数据。
前三个点对于应用程序来说通常是常量,因此唯一随堆大小而增加的是GC数据。
这种依赖性是线性的,但是“k“系数(y = kx + b)远小于1。
更一般地说,这似乎是issue 15020的后续问题,它报告了自Docker 1.7以来的类似问题
我正在运行一个简单的Scala(JVM)应用程序,它将大量数据加载到内存中,然后从内存中加载出来。
我将JVM设置为8 G堆(-Xmx8G)。我有一台内存为132 G的机器,它不能处理超过7-8个容器,因为它们的增长远远超过了我对JVM施加的8 G限制。
docker stat以前被报告为误导性的,因为它显然将文件缓存包括在总内存使用信息中)
docker stat显示每个容器本身使用的内存比JVM应该使用的要多得多。

CONTAINER CPU % MEM USAGE/LIMIT MEM % NET I/O
dave-1 3.55% 10.61 GB/135.3 GB 7.85% 7.132 MB/959.9 MB
perf-1 3.63% 16.51 GB/135.3 GB 12.21% 30.71 MB/5.115 GB

看起来好像JVM正在向操作系统请求内存,而内存是在容器中分配的,并且JVM在其GC运行时释放内存,但容器并没有将内存释放回主操作系统。因此,内存泄漏。

cld4siwp

cld4siwp2#

免责声明:我不是Maven
我最近遇到过一个生产事件,在重载下,pod的RSS有一个很大的跳跃,Kubernetes杀死了pod。没有OOM错误异常,但是Linux以最硬的方式停止了这个过程。
RSS和JVM预留的总空间之间有很大的差距。堆内存,本地内存,线程,一切看起来都很好,但是RSS很大。
我们发现,这是由于malloc内部的工作方式。内存中有很大的间隙,malloc会从那里获取内存块。如果你的机器上有很多内核,malloc会尝试适应并给予每个内核自己的空间来获取空闲内存,以避免资源争用。设置export MALLOC_ARENA_MAX=2解决了这个问题。你可以在这里找到更多关于这种情况的信息:
1.不断增长的Java进程驻留内存使用率(RSS)

  1. https://devcenter.heroku.com/articles/tuning-glibc-memory-behavior
  2. https://www.gnu.org/software/libc/manual/html_node/Malloc-Tunable-Parameters.html
  3. https://github.com/jeffgriffith/native-jvm-leaks
    另外,我不知道为什么RSS的内存会有一个跳跃。pod是建立在Sping Boot + Kafka之上的。

相关问题