1

我们的 JBoss 3.2.6 应用程序服务器存在一些性能问题,在打开详细的 GC 日志记录并使用 GCViewer 分析这些日志后,我们注意到一段时间后(服务器重新启动后 7 到 35 小时)GC 变得疯狂。似乎最初 GC 工作正常并且每隔一小时左右执行一次 GC,但在某个时候它开始变得疯狂并每分钟执行一次完整的 GC。由于这仅在我们的生产环境中发生,因此无法尝试关闭显式 GC (-XX:-DisableExplicitGC) 或修改 RMI GC 间隔,但由于这发生在几个小时后,它似乎不是由已知引起的RMI GC 问题。

有任何想法吗?

更新:

我还不能发布 GCViewer 输出,但它似乎根本没有达到最大堆限制。在 GC 发疯之前,它的 GC-ing 很好,但是当 GC 发疯时,堆不会超过 2GB(最大 24GB)。除了 RMI,还有其他方法可以触发显式 GC 吗?(我检查了我们的代码,没有调用 System.gc())

4

4 回答 4

2

你的堆填满了吗?有时,当 VM 可以释放足够的内存以防止真正的 OutOfMemoryError 但不足以真正保持应用程序稳定运行时,VM 会陷入“GC 循环”。

通常这会触发“OutOfMemoryError:超出 GC 开销限制”,但在发生这种情况之前必须超过某个阈值(98% 的 CPU 时间花在 GC 上,这超出了我的想象)。

您是否尝试过扩大堆大小?您是否检查过您的代码/使用分析器来检测内存泄漏?

于 2012-09-27T13:35:22.703 回答
1

您几乎可以肯定有内存泄漏,如果您让应用程序服务器继续运行,它最终会因 OutOfMemoryException 而崩溃。您需要使用一种内存分析工具——一个例子是VisualVM——并确定问题的根源。通常内存泄漏是由一些从不释放它们存储的对象引用的静态或全局对象引起的。

祝你好运!

更新:

重读你的问题,听起来一切都很好,然后你突然陷入了这种情况,GC 更加努力地回收空间。这听起来像是发生了一些特定的操作,消耗(并且不释放)大量的堆。

也许,正如@Tim 建议的那样,您的堆需求刚好处于最大堆大小的阈值,但根据我的经验,您需要非常幸运才能准确地达到这一点。无论如何,一些分析应该确定它是泄漏还是您只需要增加堆的大小。

于 2012-09-27T13:36:49.237 回答
0

我建议您在发生这种情况时执行堆栈转储

我经常或没有看到这种情况发生在线程数量激增的情况下。

无论如何查看堆栈转储文件并查看正在运行的内容。您可以轻松设置一些 cron 作业或监视脚本以jstack定期运行。

您还可以比较堆栈转储的大小。如果它变得非常大,那么您就会有很多线程。

如果它没有变大,您至少可以看到哪些对象(调用堆栈)正在运行。

如果这不起作用,您可以稍后使用 VisualVM 或一些花哨的 JMX 废话,但首先要开始使用,jstack因为它易于使用。

于 2012-09-27T14:07:31.867 回答
0

除了在您的应用程序中更可能发生内存泄漏之外,可能还有 1-2 个其他原因。

在 Solaris 环境中,当我将几乎所有可用的 4GB 物理内存分配给 JVM 时,我曾经遇到过这样的问题,而只给操作系统留下了大约 200-300MB。这会导致每当操作系统负载增加时,VM 进程就会突然交换到磁盘。解决方案是不超过 3.2GB。一个真正的极端案例,但也许它与您的问题相同?

这导致 GC 活动增加的原因是,大量交换会减慢 JVM 的内存管理速度,这会导致许多短期对象逃离幸存者空间,最终进入永久空间,而这又会更快地被填满。

于 2012-09-27T13:56:29.833 回答