你在
Mikhail Krestjaninoff的一篇文章中提到了"
分析Docker容器中Java内存使用"的一些线索:
(需要澄清的是,在2019年5月,三年后,
随着openJDK 8u212的发布,情况得到了改善)
Resident Set Size是指进程当前分配并使用的物理内存量(不包括交换出的页面)。它包括代码、数据和共享库(在使用它们的每个进程中都计数)。为什么docker stats信息与ps数据不同?第一个问题的答案非常简单-
Docker存在一个错误(或功能-取决于您的心情):它将文件缓存包含在总内存使用信息中。因此,我们可以避免此度量标准,并使用关于RSS的
ps
信息。那好吧-但
RSS为什么比Xmx高?从理论上讲,在Java应用程序的情况下。
RSS = Heap size + MetaSpace + OffHeap size
OffHeap包括线程堆栈、直接缓冲区、映射文件(库和jar)和JVM代码。
自JDK 1.8.40以来,我们有了本机内存跟踪器!
正如您所看到的,我已经将-XX:NativeMemoryTracking=summary
属性添加到JVM中,因此我们可以从命令行调用它:
docker exec my-app jcmd 1 VM.native_memory summary
(这是原帖作者所做的)
不要担心“未知”部分 - 看起来 NMT 是一个不成熟的工具,无法处理 CMS GC(当您使用另一个 GC 时,此部分将消失)。
请记住,NMT 显示的是“已提交”内存,而不是“常驻”内存(通过 ps 命令获得)。换句话说,一个内存页面可以被提交而不被视为常驻(直到它被直接访问)。
这意味着非堆区域(堆始终是预初始化的)的 NMT 结果可能比 RSS 值更大。
(这就是 "为什么 JVM 报告的已提交内存比 Linux 进程常驻集大小更大?" 的原因)
作为结果,尽管我们将jvm堆限制设置为256m,但我们的应用程序消耗了367M。其中“其他”164M主要用于存储类元数据、已编译代码、线程和GC数据。
前三点通常是应用程序的常量,因此随着堆大小增加的唯一变化是GC数据。
这种依赖关系是线性的,但“k”系数(y=kx+b)远小于1。
更普遍的情况是,自docker 1.7以来,类似问题已被
报告。
我正在运行一个简单的Scala(JVM)应用程序,将大量数据加载到内存中并从内存中读取。 我将JVM设置为8G堆(
-Xmx8G
)。 我有一台132G内存的机器,但由于容器远远超过了我对JVM施加的8G限制,因此最多只能处理7-8个容器。
(
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在向操作系统请求内存,然后在容器中分配内存,并且当其GC运行时释放内存,但是容器没有将内存释放回主操作系统。所以……内存泄漏。