情境:
我有一个在docker容器中运行的JVM。我使用两个工具进行了一些内存分析:1)顶部 2) Java本机内存跟踪。这些数字看起来很混乱,我试图找出导致差异的原因。
问题:
对于Java进程,RSS报告为1272MB,报告的总Java内存为790.55 MB。我怎么能解释内存的其余部分1272 - 790.55 = 481.44 MB去哪了?
为什么我想在SO this question上查看{<3}}后仍然保持此问题的开放性
我确实看到了答案,解释也很有道理。但是,在从Java NMT和pmap -x获取输出之后,我仍然无法具体映射实际驻留和物理映射的哪些Java内存地址。我需要一些具体的解释(详细步骤)来找出造成RSS和Java Total提交内存之间差异的原因。
最高输出
Java NMT
Docker内存统计信息
图表
我的docker容器运行时间超过48小时。现在,当我看到一个包含以下内容的图表时:
答案 0 :(得分:27)
你在&#34; Analyzing java memory usage in a Docker container&#34;中有一些线索。来自Mikhail Krestjaninoff:
(并且要明确,2019年5月,三年后,the situation does improves with openJDK 8u212)
R esident S et S ize是进程当前分配和使用的物理内存量(没有换出的页面)。它包括代码,数据和共享库(在使用它们的每个进程中计算)
为什么docker stats info与ps数据不同?
第一个问题的答案非常简单 - Docker has a bug (or a feature - depends on your mood):它将文件缓存包含在总内存使用信息中。因此,我们可以避免使用此指标并使用有关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显示“已提交”的内存,而不是&#34;常驻&#34; (通过ps命令获得)。换句话说,可以提交内存页面而不考虑作为常驻(直到它直接访问)。
这意味着非堆区域的NMT结果(堆总是预初始化的)可能比RSS值大。
(即{&#34; Why does a JVM report more committed memory than the linux process resident set size?&#34;来自哪里)
因此,尽管我们将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
为reported as misleading before,因为它显然包含了总内存使用信息中的文件缓存)
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运行时释放内存,但容器没有将内存释放回主要操作系统。所以......内存泄漏。