【问题标题】:JConsole heap dump much smaller than memory usageJConsole 堆转储比内存使用量小得多
【发布时间】:2019-04-22 13:56:58
【问题描述】:

我们有一些容器使用 docker 运行 java 进程。我们注意到的一件事是仅通过运行一个简单的 spring-boot 应用程序就占用了大量内存,甚至不包含我们自己的代码(只是为了尝试获得某种内存配置文件,而与我们可能引入的任何问题无关)。

我看到的是 docker/JVM 消耗的内存在 2.5 左右徘徊。我们确实在其中包含了相当数量的额外的 deps(camel、hibernate、一些 spring-boot 的 deps),但这并不是真正让我失望的东西。我看到的是,尽管 docker 说它为应用程序消耗了 2.5GB 的内存,但运行 jconsole 读取它消耗的内存高达 1GB(在 GC 后下降到 ~200MB 并缓慢攀升)。 docker 上的内存占用也保持在 GC 之后的位置(2.5GB)。

此外,当我转储堆以查看哪些类型的对象占用了该空间时,在我将 .hprof 文件加载到 MAT 后,堆看起来只有 33MB 大。这些对我来说都没有多大意义。目前,我正在查看 jconsole 中报告的非堆空间为 115MB,而堆空间为 331MB。

我已经阅读了大量(在 SO 和其他网站上)关于 JVM 内存区域的内容,以及一些专门报告堆转储可能更小的内容,但它们都没有我能说的那么远,而且除此之外,许多需要注意的建议是,每当进行堆转储时都会运行 GC,并且 MAT 具有显示或隐藏无法访问的对象的设置。所有这些都是在发帖之前考虑到的,现在我只是觉得有别的东西在起作用,我无法捕捉到自己,也没有在网上找到。

我完全预计这些数字可能会有些偏差,但在查看 docker 报告的内存使用情况时,它们在最佳情况下会下降 10 倍,而在查看 docker 报告的内存使用情况时会下降近 100 倍,这似乎是极端的.

有人知道我在这里可能缺少什么吗?

编辑:这也是一个运行 Java 8 的应用程序,尚未运行 Java 11。它在 JIRA 板上做,但尚未计划。

EDIT2:添加屏幕截图。 JConsole 屏幕截图中的峰值来自运行 GC。

【问题讨论】:

  • 如果您可以发布一些实际输出(例如 jconsole 屏幕截图)可能会有所帮助
  • 完成。感谢您的建议。
  • and off by nearly a factor of 100 when looking at the docker-reported memory usage. - 可能是 docker 报告了图像占用的整个内存,而不仅仅是你的 java 应用程序?
  • 我以为是,但实际上从 docker 容器内的进程中读取内存显示,是的,它几乎完全是 java 应用程序。作为参考,这是一个相当轻量级的高山 docker 容器。

标签: java linux spring-boot


【解决方案1】:

JConsole 为您提供已提交的内存量:3311616 KiB ~= 3GiB 如操作系统所见,这是您的 Java 进程消耗的内存量。

这与当前用于保存 Java 对象的堆大小无关,JConsole 也将其报告为 130237 kbyte ~= 130 MiB。

这也与实际存活的对象数量无关:默认情况下,当您加载堆转储时,MAT 会删除无法访问的对象。您可以通过转到 Preferences -> Memory Analyzer -> Keep Unreachable Objects 来启用该选项(请参阅the MAT documentation)。因此,如果您有很多短命对象,那么差异可能会非常大。

我看到它还报告了大约 9GiB 的最大堆。这意味着您将 Xmx 参数设置为较大的值。

Hotspot GC 不太擅长回收未使用的内存。他们倾向于使用所有可用的空间(最大堆大小,由 Xmx 设置),然后从不取消提交堆,有效地将其保留给 Java 进程,而不是将其释放给操作系统。

如果你想从操作系统的角度最小化你的进程的内存占用,我建议你设置一个较低的Xmx,可能是-Xmx1g,以免Java增长太多(当然Xmx也会需要足够高以适应您的应用程序工作量!)。

如果真的想要一个自适应堆,您也可以切换到 G1 (-XX:+UseG1GC) 和更新的 Java,因为热点团队最近交付了 some improvements

【讨论】:

  • 如果这不是问题所在,我会感到惊讶。只是猜测,选项“-XX:-UseContainerSupport”在这里与较大的 Xmx 参数有用吗?我只是好奇,因为虽然我想在进程需要时提供足够的堆空间,但我确实希望它释放内存。
  • 我不知道 UseContainerSupport。它是最近引入的选项(Java 10),因此您不会在 Java 8 中拥有它。如果我对 bugs.openjdk.java.net/browse/JDK-8146115 的理解是正确的,那么它不存在的事实就是您的 JVM 默认使用如此大的 Xmx 的原因。此外,在任何情况下,UseContainerSupport 都不会以任何方式帮助您解决堆收缩问题。只有升级到 java 12 才会!
  • 显然它也被向后移植到 8u191。 oracle.com/technetwork/java/javase/8u191-relnotes-5032181.html。感谢您提供的所有帮助,无论此选项是否有帮助。我觉得在这一点上它只是归结为选项调整。不过那是有的。我认为我们可以做到这一点。
【解决方案2】:

戴夫

操作系统监控工具会向您显示进程分配的内存量。所以这: 意味着您的 java 进程分配了 2.664G 内存(java 堆 + 元空间)

JConsole 向您显示您的代码正在“消耗”的内存(忽略元空间)

我看到了两种可能的解释:

  1. 您已将 -Xms 设置为一个巨大的值
  2. 你有很多静电 在您的元空间中加载的代码(或其他内容)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-11-23
    • 2016-08-17
    • 2021-05-13
    相关资源
    最近更新 更多