繁体   English   中英

驻留集大小 (RSS) 和 Java 总已提交 memory (NMT) 之间的差异,用于在 JVM222354FD21ZEDCD002 容器中运行的 memory (NMT)

[英]Difference between Resident Set Size (RSS) and Java total committed memory (NMT) for a JVM running in Docker container

设想:

我有一个 JVM 在 docker 容器中运行。 我使用两个工具做了一些 memory 分析:1)顶部2) Java Native Memory 跟踪 这些数字看起来令人困惑,我试图找出导致差异的原因。

问题:

Java 进程的 RSS 报告为 1272MB,总 Java Memory 报告为 79055 MB。 我如何解释 memory 1272 - 790.55 = 481.44 MB go 的 rest 在哪里?

为什么即使在查看了关于 SO 的这个问题之后,我仍想保持这个问题:

我确实看到了答案,并且解释很有意义。 However, after getting output from Java NMT and pmap -x, I am still not able to concretely map which java memory addresses are actually resident and physically mapped . 我需要一些具体的解释(带有详细的步骤)来找出导致 RSS 和 Java 总提交 memory 之间这种差异的原因。

顶部 Output

在此处输入图像描述

Java NMT

在此处输入图像描述

Docker memory 统计

在此处输入图像描述

图表

我有一个运行超过 48 小时的 docker 容器。 现在,当我看到包含以下内容的图表时:

  1. 给 docker 容器的总 memory = 2 GB
  2. Java 最大堆 = 1 GB
  3. 总提交 (JVM) = 始终小于 800 MB
  4. 使用的堆 (JVM) = 始终小于 200 MB
  5. 未使用堆 (JVM) = 始终小于 100 MB。
  6. RSS = 大约 1.1 GB。

那么,1.1 GB (RSS) 和 800 MB(Java 总提交内存)之间的 memory 是什么吃的?

在此处输入图像描述

您可以从Mikhail Krestjaninoff的分析Docker容器中的Java内存使用情况 ”中获得一些线索:

(并且要明确,在2019年5月,三年后, openJDK 8u212的情况确实有所改善

R esident S et S ize是进程当前分配和使用的物理内存量(没有换出的页面)。 它包括代码,数据和共享库(在使用它们的每个进程中计算)

为什么docker stats信息与ps数据不同?

第一个问题的答案非常简单 - Docker有一个bug(或一个功能 - 取决于你的心情) :它包括文件缓存到总内存使用信息。 因此,我们可以避免使用此指标并使用有关RSS的ps信息。

好吧,好吧 - 但为什么RSS高于Xmx?

从理论上讲,在java应用程序的情况下

RSS = Heap size + MetaSpace + OffHeap size

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

JDK 1.8.40开始,我们有Native Memory Tracker

如您所见,我已经将-XX:NativeMemoryTracking=summary属性添加到JVM中,因此我们可以从命令行调用它:

docker exec my-app jcmd 1 VM.native_memory summary

(这是OP所做的)

不要担心“未知”部分 - 似乎NMT是一个不成熟的工具,无法处理CMS GC(当您使用另一个GC时,此部分会消失)。

请记住, NMT显示“已提交”内存,而不是“常驻”(通过ps命令获取)。 换句话说,可以提交存储器页面而不考虑作为驻留者(直到它被直接访问)

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

(这就是“ 为什么JVM报告的内存比linux进程驻留集大小更多? ”)

因此,尽管我们将jvm堆限制设置为256m,但我们的应用程序消耗了367M。 “其他”164M主要用于存储类元数据,编译代码,线程和GC数据。

前三个点通常是应用程序的常量,因此随堆大小增加的唯一因素是GC数据。
这种依赖性是线性的,但“ k ”系数( y = kx + b )远小于1。


更一般地说,这似乎是问题15020 ,其报告了自docker 1.7以来的类似问题

我正在运行一个简单的Scala(JVM)应用程序,它将大量数据加载到内存中。
我将JVM设置为8G堆( -Xmx8G )。 我有一台132G内存的机器,它不能处理超过7-8个容器,因为它们超过了我对JVM施加的8G限制。

之前报告的 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运行时释放内存,但容器不会将内存释放回主操作系统。 所以...内存泄漏。

免责声明:我不是专家

我最近在重负载下发生了一起生产事件,Pod 的 RSS 大幅增加,Kubernetes 杀死了 Pod。 没有OOM错误异常,但是Linux以最硬核的方式停止了进程。

JVM 的 RSS 和总预留空间之间存在很大差距。 堆 memory,本机 memory,线程,一切看起来都不错,但是 RSS 很大。

发现这是由于malloc内部工作原理所致。 memory 中存在很大的差距,其中 malloc 从中获取了 memory 的块。 如果您的机器上有很多内核,malloc 会尝试适应并为每个内核分配自己的空间,以从中释放 memory 以避免资源争用。 设置export MALLOC_ARENA_MAX=2解决了这个问题。 您可以在此处找到有关此情况的更多信息:

  1. Java进程的常驻memory使用(RSS)
  2. https://devcenter.heroku.com/articles/tuning-glibc-memory-behavior
  3. https://www.gnu.org/software/libc/manual/html_node/Malloc-Tunable-Parameters.html
  4. https://github.com/jeffgriffith/native-jvm-leaks

PS 我不知道为什么 RSS memory 有一个跳跃。 Pod 构建在 Spring Boot + Kafka 之上。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM