Scenario:

我有一个运行在docker容器中的JVM.我用两种工具做了一些记忆分析:1)top 2)Java Native Memory Tracking.这些数字看起来令人困惑,我正试图找出造成差异的原因.

Question:

Java进程的RSS报告为1272MB,Java内存总量报告为790.55MB.我如何解释1272-790.55=481.44MB内存的其余部分go 了哪里?

Why I want to keep this issue open even after looking at 100 on SO:

我确实看到了答案,而且解释是有道理的.然而,在从JavaNMT和pmap-x获得输出后,I am still not able to concretely map which java memory addresses are actually resident and physically mapped.我需要一些具体的解释(包括详细的步骤)来找出RSS和Java总提交内存之间的差异的原因.

Top Output

enter image description here

Java NMT

enter image description here

Docker memory stats

enter image description here

Graphs

我有一个docker集装箱运行了48个多小时.现在,当我看到一个包含:

  1. 给docker容器的总内存=2 GB
  2. Java最大堆=1 GB
  3. 已提交的总容量(JVM)=始终小于800 MB
  4. 使用的堆(JVM)=始终小于200 MB
  5. 未使用堆(JVM)=始终小于100 MB.
  6. RSS=1.1GB左右.

那么,1.1GB(RSS)和800MB(Java总提交内存)之间的内存消耗了多少呢?

enter image description here

推荐答案

你在Mikhail Krestjaninoff的" Analyzing java memory usage in a Docker container"中有一些线索:

(需要明确的是,三年后的2019年5月,the situation does improves with openJDK 8u212人)

Resident Set Size是进程当前分配和使用的物理内存量(无交换页).它包括代码、数据和共享库(在使用它们的每个进程中都会计算)

为什么docker统计信息与ps数据不同?

第一个问题的答案非常简单——Docker has a bug (or a feature - depends on your mood):它将文件缓存包含在总内存使用信息中.所以,我们可以避开这个指标,使用ps条关于RSS的信息.

好吧,但是why is RSS higher than Xmx?

理论上,对于java应用程序

RSS = Heap size + MetaSpace + OffHeap size

其中OffHeap由线程堆栈、直接缓冲区、映射文件(库和JAR)和JVM代码itse组成

JDK 1.8.40开始我们有101个!

如您所见,我已经向JVM添加了-XX:NativeMemoryTracking=summary属性,所以我们可以从命令行调用它:

docker exec my-app jcmd 1 VM.native_memory summary

(这就是OP所做的)

不要担心"未知"部分——似乎NMT是一个不成熟的工具,无法处理CMS GC(当您使用另一个GC时,此部分将消失).

记住,that NMT displays “committed” memory, not "resident" (which you get through the ps command). In other words, a memory page can be committed without considering as a resident (until it directly accessed).

That means that NMT results for non-heap areas (heap is always preinitialized) might be bigger than RSS values

(这就是"Why does a JVM report more committed memory than the linux process resident set size?"的含义)

因此,尽管我们将jvm堆限制设置为256m,但我们的应用程序消耗了367M."其他"164M主要用于存储类元数据、编译代码、线程和GC数据.

前三个点通常是应用程序的常量,因此随着堆大小的增加,唯一增加的是GC数据


更一般地说,这之后似乎有issue 15020个报告了自docker 1.7以来的类似问题

我正在运行一个简单的Scala(JVM)应用程序,它将大量数据加载到内存中或从内存中取出

(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

It almost seems that the JVM is asking the OS for memory, which is allocated within the container, and the JVM is freeing memory as its GC runs, but the container doesn't release the memory back to the main OS. So... memory leak.

Linux相关问答推荐

如何在不使用LD_LIBRARY_PATH或RPATH的情况下运行链接到共享库的二进制文件?

奇怪的 Bash 脚本行为

x64 NASM 汇编程序在程序开始时显示分段错误

线程创建会在 Linux 中触发页面错误吗?它与软脏 PTE 有什么关系?

X86 程序集 - struct 点 - 存储/返回不正确?

如何在 gcc 搜索路径上防止多个版本的 Boost?

仅在 Linux 上出现 AWS RDS `flush tables` 错误的 mysqldump

如何计算文本的时差,如 YYYYMMDDHHMMSSXXX 格式,包括毫秒

如何恢复已停止的进程?

我应该如何从非 root Debian Linux 守护进程登录?

Linux time 命令输出中 real、user 和 sys 的含义

在 Bash 中从最后到第一个输出文件行

如何仅使用 SED 获得第二行

为什么 XGrabKey 会生成额外的聚焦和聚焦事件?

低功耗蓝牙:在 linux 中监听通知/指示

CLOCK_MONOTONIC 和 CLOCK_MONOTONIC_RAW 有什么区别?

用于提取 IP 地址的 Linux bash 脚本

Linux:用户名不在 sudoers 文件中

初学者如何在 Linux 中开始使用 Mono?

如何在 UNIX 中将字符串转换为整数