Java进程的内存无限增长,但MemoryMXBean报告稳定的堆和非堆大小。

47 浏览
0 Comments

Java进程的内存无限增长,但MemoryMXBean报告稳定的堆和非堆大小。

我正在与一个团队一起开发一个Java GUI应用程序,它在一个1GB的Linux目标系统上运行。

我们的问题是,我们的Java进程使用的内存会无限增长,直到Linux最终杀死Java进程。

我们的堆内存非常健康和稳定(我们已经详细分析了我们的堆)。我们还使用MemoryMXBean来监视应用程序的非堆内存使用情况,因为我们认为问题可能出在那里。然而,我们看到的是,报告的堆大小+报告的非堆大小保持稳定。

以下是在我们的目标系统上运行应用程序时,由MemoryMXBean报告的堆和非堆内存,以及Linux的top命令(驻留内存)监视的Java进程使用的总内存的演示:

启动时:

  • 已经分配了200MB堆内存
  • 已经分配了40MB非堆内存
  • Java进程使用了320MB

一天后:

  • 已经分配了200MB堆内存
  • 已经分配了40MB非堆内存
  • Java进程使用了360MB

两天后:

  • 已经分配了200MB堆内存
  • 已经分配了40MB非堆内存
  • Java进程使用了400MB

上述数字只是我们系统表现的“清晰”表示,但它们相当准确和接近现实情况。正如您所看到的,趋势是明显的。在运行应用程序数周之后,Linux系统开始因为系统内存不足而出现问题。事情开始变慢。再过几个小时,Java进程就会被杀死。

经过数月的分析和尝试,我们仍然不知所措。我觉得很难找到关于这个问题的信息,因为大多数讨论最终都会解释堆或其他非堆内存池(如Metaspace等)。

我的问题如下:

  1. 如果分解开,java进程使用的内存包括什么?(除了堆和非堆内存池)
  2. 还有哪些潜在的内存泄漏源?(本地代码?JVM开销?)哪些是通常最有可能的罪犯?
  3. 如何监测/分析这些内存?对我们来说,堆外和非堆外的所有内容目前都有点像黑匣子。

任何帮助都将不胜感激。

admin 更改状态以发布 2023年5月23日
0
0 Comments

我们似乎终于找到了我们遇到问题的根本原因。这是对于特别是导致问题的具体原因的回答,因为这对于其他人也可能有用。

简单来说:

问题是由JDK中的一个漏洞引起的,现在已修复,将随JDK 8u152一起发布。 有关漏洞报告的链接

整个故事如下:

在我第一次发布这个问题后,我们继续监视我们应用程序的内存性能,并且由vsminkov提供的XX:NativeMemoryTracking在缩小和定位泄漏内存的区域方面提供了很大帮助。

我们发现,“Thread-Arenas”区域不断增长。由于这种类型的泄漏是我们确信之前没有遇到过的,我们开始使用早期版本的Java进行测试,以查看是否从某个具体点引入了泄漏问题。

回到Java 8u73后,泄漏就消失了。虽然被迫使用较旧的JDK版本并不理想,但至少我们现在有了一个解决问题的方法。

几周后,在运行更新73时,我们发现应用程序仍在泄漏,再次开始寻找罪魁祸首。我们发现问题现在位于Class-malloc区域。

此时,我们几乎可以确定泄漏不是我们应用程序的问题,并且正在考虑联系Oracle报告可能是漏洞的问题,但随后我的一位同事偶然发现了JDK热点编译器上的这个漏洞报告:有关漏洞报告的链接。

这个Bug描述与我们所见的非常相似。根据报告中的内容,自Java 8发布以来一直存在内存泄漏问题,经过测试8u152 JDK的早期发布,我们现在相当确定这个泄漏已经解决。运行五天后,我们的应用程序的内存占用现在似乎非常稳定。Malloc区的类仍然略微增长,但现在的增长速度大约每天100 KB(与之前的数MB相比),而且只测试了5天,我不能确定它不会最终稳定下来。

我不敢确定,但似乎Class malloc和Thread arenas增长的问题是相关的。在任何时候,这两个问题都在更新152中消失了。不幸的是,这个更新计划到2017年底才会正式发布,但我们对早期发布的测试看起来到目前为止还是有希望的。

0
0 Comments

我尽力回答您的问题。

在这种情况下,我试图坚持的基本策略是监控每个可用的内存池、打开文件、套接字、缓冲池、线程数量等的最大/使用/峰值。可能会存在套接字连接/打开文件/线程泄漏情况,而您可能会错过这些情况。

在您的情况下,看起来您确实遇到了比较棘手、难以发现的本地存储器泄漏问题。

您可以尝试对内存进行分析。查看GC根并找出哪些是JNI全局引用。这可能会帮助您找出可能没有被收集的类。例如,这是awt常见的问题,可能需要显式释放组件。

要检查JVM内部存储器使用情况(不属于堆/非堆内存),-XX:NativeMemoryTracking非常方便。它可以让您跟踪线程堆栈大小、GC/编译器开销等等。它最伟大的一点是您可以在任何时间点创建一个基线,然后跟踪自基线以来的存储器差异。

# jcmd  VM.native_memory baseline
# jcmd  VM.native_memory summary.diff scale=MB
Total:  reserved=664624KB  -20610KB, committed=254344KB -20610KB
...

您还可以使用JMX命令com.sun.management:type=DiagnosticCommand/vmNativeMemory来生成这些报告。

您还可以深入研究pmap -x 和/或procfs内容。

0