场景:
我有一个运行在Docker容器中的JVM。 我做了一些内存分析,使用两个工具:1) 顶部 2) Java本机内存跟踪 。 数字看起来令人困惑,我试图找出造成差异的是什么。
题:
Java进程报告RSS为1272MB,总Java内存报告为790.55 MB。 我怎样才能解释其余的内存1272 – 790.55 = 481.44 MB去哪里?
为什么我要在SO上看到这个问题之后继续保持这个问题:
我确实看到了答案,解释是有道理的。 但是,从Java NMT和pmap -x获得输出后, 我仍然无法具体映射哪些java内存地址实际上是驻留和物理映射的 。 我需要一些具体的解释(详细的步骤)来找出导致RSS和Java Total Commit内存之间的区别的原因。
顶部输出
Java NMT
Docker内存统计信息
图表
我有一个docker集装箱运行超过48小时。 现在,当我看到一个包含以下内容的图:
那么,在1.1 GB(RSS)和800 MB(Java Total Commit内存)之间吃什么呢?
您在Mikhail Krestjaninoff的 “ 分析Docker容器中的Java内存使用情况 ”中有一些线索:
R esident S et Size是进程分配和使用的物理内存量(未换出页面)。 它包括代码,数据和共享库(在使用它们的每个进程中计算)
为什么docker统计信息与ps数据有所不同?
对第一个问题的回答非常简单 – Docker有一个错误(或者一个功能 – 取决于你的心情) :它包括文件缓存到总的内存使用信息中。 所以,我们可以避免这个指标,并使用关于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 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
为误导 ,因为它显然包含了文件缓存到总的内存使用信息中)
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运行时释放内存,但容器不会将内存释放回主操作系统。 所以…内存泄漏。