【问题标题】:GC graph shows there is a memory leak but unable to track in the dumpGC 图显示存在内存泄漏但无法在转储中跟踪
【发布时间】:2025-12-06 11:10:01
【问题描述】:

我们的应用程序中有一个 Java Micorservice,它连接到 Postgres 和 Phoenix。我们使用的是 Spring Boot 2.x。

问题是我们正在为我们的应用程序执行大约 8 小时的耐久性测试,我们可以观察到使用的堆在不断增加,尽管我们使用了推荐的 VM 参数建议,看起来像是内存泄漏。我们分析了堆转储,但根本原因对我们来说并不完全清楚,一些专家可以根据结果提供帮助吗?

我们实际使用的虚拟机参数是:

-XX:ConcGCThreads=8 -XX:+DisableExplicitGC -XX:InitialHeapSize=536870912 -XX:InitiatingHeapOccupancyPercent=45 -XX:MaxGCPauseMillis=1000 -XX:MaxHeapFreeRatio=70 -XX:MaxHeapSize=536870912 -XX:MinHeapFreeRatio=40 -XX:ParallelGCThreads=16 -XX:+PrintAdaptiveSizePolicy -XX:+PrintGC -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:StringDeduplicationAgeThreshold=1 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX: +UseG1GC -XX:+UseStringDeduplication

我们预计 GC 日志中使用的堆应该是平坦的,但是内存消耗并没有释放,并且还在不断增加。

堆转储:

GC 图:

【问题讨论】:

  • 我们使用了 VM 参数的推荐建议”谁根据什么理由向您推荐了这些参数?他们是否说“这些是使每个应用程序运行得比默认设置更好的神奇参数”?但无论如何,只要你没有遇到OutOfMemoryError,就没有理由假设存在内存泄漏。事实上,没有这样的错误证明“使用的堆在不断增加”。
  • 感谢您的意见,这些不是任何专家推荐的建议,但根据我们的多次运行和观察以及文档中提供的定义,我们得出结论,这应该是我们可以使用适当的组合来更好地执行。
  • 您还说“没有理由假设存在内存泄漏”。但是,如果我们继续执行 24 小时并且使用的堆正向顶部移动,是否会有一段时间达到其最大限制和完全 GC 的机会。我们还观察到(在不同的运行中)即使在完全 GC 之后也只有部分堆内存被释放。现在有内存不足的可能吗?
  • 一些内存被永久分配,一些内存被分配和释放是正常的行为。这并不排除永久分配的内存包含一些您不想保留的对象,但只要永久分配的内存没有上升到应用程序失败的程度(例如使用OutOfMemoryError),您没有严重的问题。而且我们无法从内存统计中看到是否有不需要的对象。甚至堆转储(更不用说屏幕截图)也无济于事,因为我们不知道哪些对象应该存在,哪些不存在。
  • 非常感谢 Holger,我们现在可以使用 IHOP 和 Newratio 获得一个不错的 GC 图。使用的堆比以前更好地释放。

标签: garbage-collection jvm heap-memory


【解决方案1】:

我不确定您在上面使用的是哪个工具,但我会在堆中寻找支配者层次结构。 Eclipse MAT 是分析堆转储的好工具,它可以为您指明实际持有内存的方向,您可以决定是否要将其归类为泄漏。不管你贴什么标签,如果应用程序在一段时间后因为内存不足而崩溃,那么这是一个问题。

blog 还讨论了诊断此类问题。

【讨论】: