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

时间:2016-07-26 19:04:47

标签: java linux memory docker jvm

情境:

我有一个在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提交内存之间差异的原因。

最高输出

enter image description here

Java NMT

enter image description here

Docker内存统计信息

enter image description here

图表

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

  1. 给予docker容器的总内存= 2 GB
  2. Java Max Heap = 1 GB
  3. 提交总数(JVM)=始终小于800 MB
  4. 堆使用(JVM)=始终小于200 MB
  5. 使用的非堆(JVM)=始终小于100 MB。
  6. RSS =约1.1 GB。
  7. 那么,在1.1 GB(RSS)和800 MB(Java Total committed memory)之间占用的内存是什么?

    enter image description here

1 个答案:

答案 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 statreported 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运行时释放内存,但容器没有将内存释放回主要操作系统。所以......内存泄漏。