问题描述
场景:
我有一个在 docker 容器中运行的 JVM.我使用两个工具进行了一些内存分析:1) top 2) Java Native Memory Tracking.数字看起来令人困惑,我试图找出导致差异的原因.
问题:
Java 进程的 RSS 报告为 1272MB,Java 总内存报告为 790.55MB.我如何解释剩余的内存 1272 - 790.55 = 481.44 MB 去了哪里?
为什么即使在查看了关于 SO 的
Java NMT
Docker 内存统计
图表
我有一个 docker 容器运行了超过 48 小时.现在,当我看到一个包含以下内容的图表时:
- 分配给 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 总提交内存)之间的内存是什么?
您在分析 Docker 容器中的 Java 内存使用情况",来自 Mikhail Krestjaninoff:
(需要明确的是,三年后的 2019 年 5 月,openJDK 的情况确实有所改善8u212)
Resident Set Size 是进程当前分配和使用的物理内存量(没有换出页面).它包括代码、数据和共享库(在每个使用它们的进程中都计算在内)
为什么 docker stats 信息与 ps 数据不同?
第一个问题的答案很简单——Docker 有一个 bug(或一个特性——取决于你的心情):它将文件缓存包含在总内存使用信息中.所以,我们可以避免这个指标并使用关于 RSS 的 ps
信息.
好吧 - 但是为什么 RSS 比 Xmx 高?
理论上,在java应用程序的情况下
RSS = Heap size + MetaSpace + OffHeap size
其中 OffHeap 由线程堆栈、直接缓冲区、映射文件(库和 jar)和 JVM 代码组成
自 JDK 1.8.40 我们有本机内存跟踪器!
如您所见,我已经向 JVM 添加了 -XX:NativeMemoryTracking=summary
属性,因此我们可以从命令行调用它:
docker exec my-app jcmd 1 VM.native_memory 总结
(这就是 OP 所做的)
不要担心未知"部分 - 似乎 NMT 是一个不成熟的工具,无法处理 CMS GC(当您使用另一个 GC 时,此部分会消失).
请记住,NMT 显示的是已提交"的内存,而不是常驻"(您可以通过 ps 命令获得).换句话说,一个内存页可以被提交而不考虑作为一个常驻者(直到它被直接访问).
这意味着非堆区域(堆始终预初始化)的 NMT 结果可能大于 RSS 值.
(这就是为什么 JVM 报告提交的内存比 linux 进程驻留集大小更多?"的地方在)
因此,尽管我们将 jvm 堆限制设置为 256m,但我们的应用程序消耗了 367M.其他"164M 主要用于存储类元数据、编译代码、线程和 GC 数据.
对于应用程序来说,前三点通常是常量,因此唯一随着堆大小而增加的就是 GC 数据.
这种依赖性是线性的,但k
"系数(y = kx + b
)远小于 1.
更一般地说,这之后似乎是 issue 15020 报告了类似的问题自 docker 1.7
我正在运行一个简单的 Scala (JVM) 应用程序,该应用程序将大量数据载入和载入内存.
我将 JVM 设置为 8G 堆(-Xmx8G
).我有一台内存为 132G 的机器,它不能处理超过 7-8 个容器,因为它们的增长远远超过了我对 JVM 施加的 8G 限制.
(docker stat
是 之前被报告误导,因为它显然将文件缓存包含在总内存使用信息中)
docker stat
显示每个容器本身使用的内存比 JVM 应该使用的内存多得多.例如:
容器 CPU % MEM 使用/限制 MEM % NET I/Odave-1 3.55% 10.61 GB/135.3 GB 7.85% 7.132 MB/959.9 MBperf-1 3.63% 16.51 GB/135.3 GB 12.21% 30.71 MB/5.115 GB
JVM 几乎似乎在向操作系统请求内存,这些内存是在容器内分配的,并且 JVM 在其 GC 运行时正在释放内存,但容器不会将内存释放回主操作系统.所以...内存泄漏.
Scenario:
I have a JVM running in a docker container. I did some memory analysis using two tools: 1) top 2) Java Native Memory Tracking. The numbers look confusing and I am trying to find whats causing the differences.
Question:
The RSS is reported as 1272MB for the Java process and the Total Java Memory is reported as 790.55 MB. How can I explain where did the rest of the memory 1272 - 790.55 = 481.44 MB go?
Why I want to keep this issue open even after looking at this question on SO:
I did see the answer and the explanation makes sense. 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. I need some concrete explanation (with detailed steps) to find whats causing this difference between RSS and Java Total committed memory.
Top Output
Java NMT
Docker memory stats
Graphs
I have a docker container running for most than 48 hours. Now, when I see a graph which contains:
- Total memory given to the docker container = 2 GB
- Java Max Heap = 1 GB
- Total committed (JVM) = always less than 800 MB
- Heap Used (JVM) = always less than 200 MB
- Non Heap Used (JVM) = always less than 100 MB.
- RSS = around 1.1 GB.
So, whats eating the memory between 1.1 GB (RSS) and 800 MB (Java Total committed memory)?
You have some clue in "Analyzing java memory usage in a Docker container" from Mikhail Krestjaninoff:
(And to be clear, in May 2019, three years later, the situation does improves with openJDK 8u212 )
RSS = Heap size + MetaSpace + OffHeap size
docker exec my-app jcmd 1 VM.native_memory summary
(This is what the OP did)
(that is where "Why does a JVM report more committed memory than the linux process resident set size?" comes in)
More generally, this seems to be followed by issue 15020 which reports a similar issue since docker 1.7
(docker stat
was reported as misleading before, as it apparently includes file caches into the total memory usage info)
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
这篇关于在 Docker 容器中运行的 JVM 的驻留集大小 (RSS) 和 Java 总提交内存 (NMT) 之间的区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!