当前位置: 首页 > 知识库问答 >
问题:

docker运行java进程所消耗的无法解释的额外内存

金子轩
2023-03-14
  • 在docker容器中运行的Java应用程序
  • 没有调用本机代码,没有已处理的启动,没有DLL/。所以文件被引用了
  • JVM参数:-Xmx256m-XX:NativeMemoryTracking=summary
  • Docker硬盘内存限制设置为768m
  • JVM似乎正常(正常的GC周期,没有内存泄漏,没有OOM)
  • Docker内存一直在增长,直到达到硬限制(768m),导致终止并重新启动容器
  • 为什么docker统计内存持续增长(导致每天都遇到硬内存限制),尽管JVM似乎在其限制范围内运行。

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

在JVM方面,我们没有注意到任何特别之处:

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.8MiB跃升到649.6MiB
  • JVM提交的本机内存从472227KB跳到510928KB
  • docker统计数据和JVMpromise的本机内存之间的差距似乎正在扩大。(649.6MB-510928KB的
  • 在此期间,JVM统计数据保持正常

所以我不知道我还可以在JVM端调试什么。我了解java需要的不仅仅是堆(因此本机内存跟踪),但在jvm本机内存跟踪器报告的内容和docker统计数据所看到的内容之间仍然有大约150m的差距。我如何在内存的去向中获得更多见解?


共有2个答案

李飞翼
2023-03-14

答:请仔细阅读janisz的答案,并遵循链接,对于在容器中或在cGroup下使用Java的人来说,这是很多非常重要的信息。

B.主要问题是JVM没有看到容器上的内存限制:它认为自己拥有主机操作系统的全部可用内存。当内核/docker试图消耗超过cgroup限制所允许的内存时,内核/docker会因违反cgroup内存限制promise而杀死容器。这就是-XX:UseContainerSupport和较早的-XX:UseCGroupMemoryLimitForHeap标志应该解决的问题:让JVM知道真正的限制是什么。

-Xmx标志并不限制JVM(作为Linux进程)从操作系统需要的所有内存。JVM本身的所有机制,包括Java堆栈、元空间、加载的代码等(正如janisz在回答中所讨论的),都会占用内存空间。

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

臧烨烁
2023-03-14

JVM报告的内存不是一切。

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

  • 原生JRE
  • 烫发/metaspace
  • JIT字节码
  • JNI
  • NIO
  • 线程

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

让我们简要介绍一下JVM是如何根据其运行的节点上可用的处理器/内核数量进行调整的。实际上有很多参数,默认情况下是基于核心计数初始化的。

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

因此,如果JVM在32核节点上运行(其中一个没有覆盖默认值),JVM将生成32个垃圾收集线程、32个JIT编译器线程等等…。来源

为了防止这个问题,您应该使用UseContainerSupport(在Java 10中默认启用8u191后可用),并且可能使用-XX:MaxRAMPercentage=90.0或更少,具体取决于您观察到的总使用内存。要了解更多信息,请查看这个和那个。

我强烈推荐:"没有人把Java放在容器里:Ken Sipe"来自JavaZone

 类似资料:
  • 我正在调试一个Azure Web应用程序上无法解释的内存消耗。我花了很多时间使用dotmemory、perfview和诊断工具挖掘日志、内存转储,但仍然无法理解为什么我们的应用程序内存一整天都在稳步增长。 我在堆中验证了多次,以确保请求前后的应用程序对象/模块都被释放,上面的屏幕截图显示了这一点。 如果你能分享一些关于如何找到消耗内存的东西的建议,我会的

  • 问题内容: 我是Go的新手,它试图弄清楚它如何管理内存消耗。 我在一个测试项目中的内存出现问题。我不明白为什么当我的程序长时间运行时,Go使用越来越多的内存(从不释放它)。 我正在运行下面提供的测试用例。第一次分配后,程序将使用近350 MB的内存(根据ActivityMonitor)。然后,我尝试释放它,ActivityMonitor显示内存消耗增加了一倍。为什么? 我正在使用Go 1.0.3在

  • 问题内容: 我需要监视应用程序产生的线程消耗的内存量。如果贪婪的线程消耗太多内存,则想法是采取纠正措施。我已提到Java线程占用多少内存?。关于该链接的建议之一是在我尝试以下工作时使用。 我在四个线程上运行了很长时间。尽管作业不会连续地累积内存,但是所返回的值会不断增加,甚至不会下降。这意味着不会返回线程使用的堆上的实际内存量。它返回自线程启动以来在堆上为线程分配的内存总量。我的平台详细信息如下:

  • 我需要监控应用程序生成的线程所消耗的内存量。如果贪婪的线程占用了太多内存,那么我们可以采取纠正措施。我提到了我的java线程需要多少内存?。关于该链接的建议之一是在ThreadMXBean中使用getThreadAllocatedBytes 我用以下作业试验了getThreadAllocatedBytes。 我在四个线程上运行了相当长的时间。虽然作业不会连续累积内存,但getThreadAlloc

  • 关于Java应用程序使用的驻留内存,我有两个问题。 一些背景细节: 我用-xms2560m-xmx2560m设置了一个java应用程序。 java应用程序在容器中运行。k8s允许容器最多消耗4GB. 堆:应用程序的工作方式似乎是使用所有内存,然后释放,然后使用等等。 这张快照说明了这一点。Y列是空闲堆内存。(由应用程序通过)提取) 我还可以使用HotSpotDiagnosticMXBean来确认它

  • 我有一个很小的java控制台应用程序,我想在内存使用方面进行优化。它是在Xmx设置为仅64MB的情况下运行的。根据不同的监视工具(htop、ps、pmap、Dynatrace)显示进程的总体内存使用量超过250MB。我主要在Ubuntu18上运行它(也在其他操作系统上测试)。 我使用了-xx:nativeMemoryTracking,java param和jcmd的本地内存跟踪,以找出为什么在堆之