Docker 容器中运行的 JVM 的驻留集大小 (RSS) 和 Java 总已提交内存 (NMT) 之间的差异
- 2024-11-07 08:55:00
- admin 原创
- 27
问题描述:
设想:
我有一个在 docker 容器中运行的 JVM。我使用两个工具进行了一些内存分析:1) top 2) Java 本机内存跟踪。这些数字看起来很混乱,我试图找出导致差异的原因。
问题:
报告称 Java 进程的 RSS 为 1272MB,而总 Java 内存为 790.55 MB。我该如何解释剩余的内存 1272 - 790.55 = 481.44 MB 去了哪里?
为什么即使在看了SO上的这个问题之后我仍想保持这个问题开放:
我确实看到了答案,解释也很有道理。但是,在从 Java NMT 和 pmap -x 获取输出后,我仍然无法具体映射哪些 Java 内存地址实际驻留并物理映射。我需要一些具体的解释(包括详细步骤)来找出导致 RSS 和 Java Total submitted memory 之间差异的原因。
顶部输出
Java NMT
Docker 内存统计
图表
我有一个运行了超过 48 小时的 Docker 容器。现在,当我看到包含以下内容的图表时:
分配给 docker 容器的总内存 = 2 GB
Java 最大堆 = 1 GB
总已提交 (JVM) = 始终小于 800 MB
堆使用量 (JVM) = 始终小于 200 MB
非堆使用量(JVM)= 始终小于 100 MB。
RSS = 大约 1.1 GB。
那么,是什么消耗了 1.1 GB(RSS)和 800 MB(Java 总已提交内存)之间的内存?
解决方案 1:
您可以在Mikhail Krestjaninoff的“分析 Docker 容器中的 Java 内存使用情况”中找到一些线索:
(需要明确的是,三年后的 2019 年 5 月,随着 openJDK 8u212 的推出,情况确实有所改善)
常驻内存大小是进程当前分配和使用的物理内存量(不包括换出页面)。它包括代码、数据和共享库(在每个使用它们的进程中都会计算)
为什么docker stats信息和ps数据不同?
第一个问题的答案很简单 - Docker 有一个错误(或功能 - 取决于您的心情):它将文件缓存包含在总内存使用信息中。因此,我们可以避免使用这个指标并使用
ps
有关 RSS 的信息。嗯,好的 - 但是为什么 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 似乎正在向 OS 请求在容器内分配的内存,并且 JVM 在其 GC 运行时正在释放内存,但容器不会将内存释放回主 OS。所以... 内存泄漏。
解决方案 2:
免责声明:我不是专家
我最近遇到了一个生产事故,在高负载下,pod 的 RSS 大幅增加,Kubernetes 终止了这些 pod。没有 OOM 错误异常,但 Linux 以最硬核的方式停止了该进程。
RSS 与 JVM 预留的总空间之间存在很大差距。堆内存、本机内存、线程,一切看起来都很好,但 RSS 很大。
我们发现这是由于 malloc 内部的工作方式造成的。内存中存在很大的间隙,malloc 可以从中获取大块内存。如果您的机器上有很多核心,malloc 会尝试适应并为每个核心提供各自的空间来获取可用内存,以避免资源争用。设置export MALLOC_ARENA_MAX=2
解决了这个问题。您可以在此处找到有关此情况的更多信息:
Java 进程的常驻内存使用量 (RSS) 不断增长
https://devcenter.heroku.com/articles/tuning-glibc-memory-behavior
https://www.gnu.org/software/libc/manual/html_node/Malloc-Tunable-Parameters.html
PS 我不知道 RSS 内存为何会激增。Pod 是基于 Spring Boot + Kafka 构建的。
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件