首页 > 解决方案 > java进程随着时间的推移消耗更多内存但没有内存泄漏

问题描述

我的 java 服务在 16 GB RAM 主机上运行,​​ -Xms 和 -Xmx 设置为 8GB。主机正在运行其他一些进程。

我注意到我的服务随着时间的推移会消耗更多的内存。ps aux | awk '{print $6/1024 " MB\t\t" $11}' | sort -n我在主机上运行了这个命令并记录了我的 java 服务的内存使用情况。

服务启动时,它使用了大约 8GB 内存(因为 -Xms 和 -Xmx 设置为 8GB),但一周后,它使用了大约 9GB+ 内存。它每天消耗大约 100MB 更多的内存。

我做了一个堆转储。我重新启动了服务并进行了另一个堆转储。我比较了这两个转储,但堆使用量没有太大差异。转储显示该服务在重新启动前使用了大约 1.3GB,在重新启动后使用了大约 1.1GB。

从进程内存使用情况来看,我的服务随着时间的推移消耗了更多内存,但堆转储中没有报告。如何识别服务中内存使用量的增加?

我将 -Xms 和 -Xmx 设置为 8GB。主机有 16GB 内存。我是否将最小/最大堆设置得太高(主机上总内存的 50%)?这会引起任何问题吗?

标签: javamemory-leaksjvm

解决方案


好的,您已经告诉 JVM,它最多可以为堆使用 8GB,并且您观察到内存使用量从 1.1GB 增加到 1.3GB。这本身并不是一个迹象或问题。当然,JVM 并没有像您所说的那样使用任何地方的内存。

要注意的第二件事是尚不清楚您如何测量内存使用情况。您应该知道 JVM 使用大量内存,而不是 Java 堆内存。这包括:

  • java可执行文件本身使用的内存。
  • 用于保存本机库的内存。
  • 用于保存字节码和 JIT 编译的本机代码的内存(在“元空间”中)
  • 线程堆栈
  • (通常)本机代码请求的堆外内存分配。
  • 内存映射文件和共享内存段。

报告了其中的一些用法(如果您使用正确的工具)。

第三件事是 Java 堆实际使用的内存可能会有很大差异。GC 通常通过将活动对象从一个“空间”复制到另一个来工作,因此它需要大量的可用空间来执行此操作。然后,一旦完成运行,GC 就会查看(现在)有多少可用空间与已用空间的比率。如果该比率太小,它会向操作系统请求更多内存。因此,即使实际使用量(在非垃圾对象中)只是逐渐增加,总内存使用量也可能会大幅增加。由于各种“热身”效应,这对于最近才启动的 JVM 来说很可能。

最后,您提供的证据并没有说(对我来说!)没有内存泄漏。我认为您需要将堆转储进一步分开。我建议在启动后 2 小时进行一次转储,然后在 2 小时或更长时间后进行第二次转储。这会给你足够的“泄漏”来显示在转储的比较中。

从进程内存使用情况来看,我的服务随着时间的推移消耗了更多内存,但堆转储中没有报告。如何识别服务中内存使用量的增加?

我不认为你需要这样做。我认为总体内存使用量从 1.1GB 增加到 1.3GB 是一个红鲱鱼。

相反,我认为您应该关注其他证据所指向的内存泄漏。见我上面的建议。

我是否将最小/最大堆设置得太高(主机上总内存的 50%)?这会引起任何问题吗?

嗯......当堆满时,更大的堆将会有更明显的性能下降。另一方面,更大的堆意味着填充需要更长的时间......假设您有内存泄漏......这意味着诊断问题可能需要更长的时间,或者确保您已修复它。

但另一方面,这可能根本不是内存泄漏。它也可能是您的应用程序或第三方库缓存的东西。正确实现的缓存可能会使用大量内存,但如果堆太接近满,它应该通过断开链接1并逐出缓存数据来响应。因此,不是内存泄漏......假设。


1 - 或者如果你使用SoftReferences,GC 会为你破坏它们。


推荐阅读