运行 java 进程的 docker 消耗的无法解释的额外内存附加信息

2022-09-01 17:50:48

我们拥有:

  • 在 docker 容器中运行的 Java 应用
  • 没有调用本机代码,没有已处理的启动,没有引用的 DLL / .so 文件。
  • JVM args :-Xmx256m -XX:NativeMemoryTracking=summary
  • Docker 硬内存限制设置为768m
  • JVM 似乎正常(正常的 GC 周期,无内存泄漏,无 OOM)
  • Docker 内存不断增长,直到达到硬限制 (),从而导致终止并重新启动容器。768m

问题 :

  • 为什么 docker 统计信息内存会继续增长(导致硬内存限制每天都在被击中),尽管 JVM 似乎在其限制范围内运行。

enter image description here

  • 对于其他微服务,我们看不到此行为

enter image description here

JVM

在JVM方面,我们没有注意到任何特别的东西:

enter image description here

Docker 统计信息输出:

492.8MiB / 768MiB     64.17%              

[ec2-user@ip-10-180-28-222 ~]$ docker exec 34d7 jcmd 1 VM.native_memory summary
1:

Native Memory Tracking:

Total: reserved=1731355KB, committed=472227KB
-                 Java Heap (reserved=262144KB, committed=262144KB)
                            (mmap: reserved=262144KB, committed=262144KB)

-                     Class (reserved=1131805KB, committed=92829KB)
                            (classes #16224)
                            (malloc=7453KB #20996)
                            (mmap: reserved=1124352KB, committed=85376KB)

-                    Thread (reserved=29932KB, committed=29932KB)
                            (thread #30)
                            (stack: reserved=29772KB, committed=29772KB)
                            (malloc=94KB #151)
                            (arena=66KB #55)

-                      Code (reserved=255659KB, committed=35507KB)
                            (malloc=6059KB #9814)
                            (mmap: reserved=249600KB, committed=29448KB)

-                        GC (reserved=15369KB, committed=15369KB)
                            (malloc=5785KB #547)
                            (mmap: reserved=9584KB, committed=9584KB)

-                  Compiler (reserved=190KB, committed=190KB)
                            (malloc=59KB #858)
                            (arena=131KB #6)

-                  Internal (reserved=7849KB, committed=7849KB)
                            (malloc=7817KB #18468)
                            (mmap: reserved=32KB, committed=32KB)

-                    Symbol (reserved=20018KB, committed=20018KB)
                            (malloc=17325KB #175818)
                            (arena=2693KB #1)

-    Native Memory Tracking (reserved=3558KB, committed=3558KB)
                            (malloc=10KB #120)
                            (tracking overhead=3548KB)

-               Arena Chunk (reserved=4830KB, committed=4830KB)
                            (malloc=4830KB)

跑步约20小时后

649.6MiB / 768MiB     84.59%               

[ec2-user@ip-10-180-28-222 ~]$ docker exec 34d7 jcmd 1 VM.native_memory summary
1:

Native Memory Tracking:

Total: reserved=1741020KB, committed=510928KB
-                 Java Heap (reserved=262144KB, committed=262144KB)
                            (mmap: reserved=262144KB, committed=262144KB)

-                     Class (reserved=1138319KB, committed=100495KB)
                            (classes #16390)
                            (malloc=7823KB #30851)
                            (mmap: reserved=1130496KB, committed=92672KB)

-                    Thread (reserved=30996KB, committed=30996KB)
                            (thread #31)
                            (stack: reserved=30800KB, committed=30800KB)
                            (malloc=97KB #156)
                            (arena=99KB #57)

-                      Code (reserved=261330KB, committed=69062KB)
                            (malloc=11730KB #16047)
                            (mmap: reserved=249600KB, committed=57332KB)

-                        GC (reserved=15363KB, committed=15363KB)
                            (malloc=5779KB #334)
                            (mmap: reserved=9584KB, committed=9584KB)

-                  Compiler (reserved=223KB, committed=223KB)
                            (malloc=92KB #1246)
                            (arena=131KB #6)

-                  Internal (reserved=8358KB, committed=8358KB)
                            (malloc=8326KB #18561)
                            (mmap: reserved=32KB, committed=32KB)

-                    Symbol (reserved=20253KB, committed=20253KB)
                            (malloc=17527KB #177997)
                            (arena=2725KB #1)

-    Native Memory Tracking (reserved=3846KB, committed=3846KB)
                            (malloc=10KB #127)
                            (tracking overhead=3836KB)

-               Arena Chunk (reserved=188KB, committed=188KB)
                            (malloc=188KB)

观察

观察20小时后,我们所知道的:

  • Docker 统计数据从 跳转到492.8MiB649.6MiB
  • JVM 提交的本机内存从 跳转到472227KB510928KB
  • Docker 统计信息和 JVM 提交的本机内存之间的差距似乎正在扩大。(在哪里以及为什么会增长)649.6MiB - 510928KB
  • 在此期间,JVM 统计信息保持正常。

所以我不知道在JVM端我还能调试什么。我知道java需要的不仅仅是堆(因此是原生内存跟踪),但是在jvm原生内存跟踪器报告的内容和docker统计数据所看到的之间仍然有大约150m的差距。我怎样才能更深入地了解记忆的去向?


答案 1

JVM 报告的内存不是全部。

JVM 的内存占用

那么,是什么导致了JVM内存占用?我们大多数运行过Java应用程序的人都知道如何设置最大堆空间。但实际上,对内存占用的贡献还有很多:

  • 原生 JRE
  • 烫发/元空间
  • JIT 字节码
  • 断续器
  • 蔚来汽车
  • 线程

当我们想要使用 Docker 容器设置内存限制时,需要记住很多。并且还将容器内存限制设置为最大堆空间,可能还不够...

JVM 和 CPU

让我们简要地看一下 JVM 如何根据运行它的节点上可用的处理器/内核数量进行调整。实际上有许多参数,默认情况下,这些参数是根据内核计数初始化的。

  • ♯ 的 JIT 编译器线程
  • ♯ 垃圾回收线程
  • ♯ 公共分叉连接池中的线程...

因此,如果JVM在32个核心节点上运行(并且其中一个没有覆盖默认值),JVM将生成32个垃圾回收线程,32个JIT编译器线程,....

为了防止出现此问题,您应该使用 +UseContainerSupport(在 Java 10 中默认启用 8u191 后可用),并且可能更少,具体取决于您观察到的总使用内存。有关详细信息,请参阅此内容该内容-XX:MaxRAMPercentage=90.0

我强烈推荐:来自JavaZone的“没有人把Java放在容器中:Ken Sipe”


答案 2

一个。请仔细阅读janisz的答案并按照链接进行操作,对于在容器中或以其他方式在cgroups下使用Java的人来说,这是很多非常重要的信息。

B.主要问题是JVM在容器上看不到内存限制:它认为它具有主机操作系统的全部可用内存。当它尝试消耗的内存超过 cgroup 限制允许的内存时,内核/docker 会因违反 cgroup 内存限制承诺而终止容器。这就是旧标志应该解决的问题:让JVM知道真正的限制是什么。-XX:+UseContainerSupport-XX:+UseCGroupMemoryLimitForHeap

附加信息

该标志不限制 JVM(作为 Linux 进程)从操作系统所需的所有内存。JVM本身的所有machniation,包括Java堆栈,元空间,加载的代码等(如janisz的答案中所述)也确实占用了内存空间。-Xmx

不幸的是,JVM喜欢从操作系统中获取尽可能多的内存,并且更喜欢占用更多的内存(如果它认为它是可用的),然后重用现有的(可能是可释放的)内存。对此行为的改进(即不假设JVM是系统中唯一的参与者)计划作为Java 12附带的新G1垃圾回收器的一部分,但除非您有它为您工作,否则JVM的内存使用量将始终增长,并且更愿意消耗所有可用的可用内存, 假设操作系统的唯一目的是为它正在运行的这个JVM提供服务。