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

Ser*_*nic 14 java linux memory memory-leaks

我正在与一个开发在1GB Linux目标系统上运行的Java GUI应用程序的团队合作.

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

我们的堆内存健康稳定.(我们已广泛地分析了我们的堆)我们还使用MemoryMXBean来监视应用程序的非堆内存使用情况,因为我们认为问题可能就在那里.但是,我们看到报告的堆大小+报告的非堆大小保持稳定.

下面是一个示例,说明在我们的目标系统上运行具有1GB RAM的应用程序时的数字(MemoryMXBean报告的堆和非堆,使用Linux的top命令(常驻内存)监视的Java进程使用的总内存):

在启动时:

  • 提交200 MB堆
  • 40 MB非堆已提交
  • java进程使用的320 MB

1天后:

  • 提交200 MB堆
  • 40 MB非堆已提交
  • java进程使用的360 MB

2天后:

  • 提交200 MB堆
  • 40 MB非堆已提交
  • java进程使用的400 MB

上面的数字只是我们系统表现的"清晰"表示,但它们相当准确且接近现实.如您所见,趋势很明显.运行应用程序几周后,由于系统内存不足,Linux系统开始出现问题.事情开始放缓.再过几个小时,Java进程就被杀了.

经过几个月的分析并试图理解这一点,我们仍然处于亏损状态.我觉得很难找到有关此问题的信息,因为大多数讨论最终都会解释堆或其他非堆内存池.(像Metaspace等)

我的问题如下:

  1. 如果你把它分解,java进程使用的内存包括什么?(除了堆和非堆内存池之外)

  2. 哪些其他潜在来源存在内存泄漏?(本机代码?JVM开销?)一般来说哪些是最可能的罪魁祸首?

  3. 如何监控/分析这个内存?堆+非堆外的所有东西目前对我们来说都是一个黑盒子.

任何帮助将不胜感激.

vsm*_*kov 5

我会尝试部分回答你的问题。

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

在您的情况下,您似乎确实遇到了本机内存泄漏的问题,这是非常令人讨厌且难以发现的。

您可以尝试分析内存。查看GC根并找出哪些是JNI全局引用。它可以帮助您找出哪些课程可能不会被收集。例如,这是一个常见问题awt可能需要显式组件处理。

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

# jcmd <pid> VM.native_memory baseline
# jcmd <pid> VM.native_memory summary.diff scale=MB

Total:  reserved=664624KB  -20610KB, committed=254344KB -20610KB
...
Run Code Online (Sandbox Code Playgroud)

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

而且...您可以更深入地检查pmap -x <pid>和/或procfs内容。