申请背景:
我们在 Tomcat 服务器上有一个供多个用户使用的 Java Web 应用程序。我们有 30 多个客户服务器正在运行。
问题:
在一个特定的客户身上,我们遇到了一个关于内存使用的奇怪现象。系统运行了几个小时/几天,相当不错,但一段时间后,服务器性能由于 CPU 负载而下降,我认为这是因为 MemoryUsage / GC。
见解:
从屏幕截图中可以看出,我们有大约 14GB 可用内存,应用程序保留了大约 5GB 的堆,并且还使用了 5GB。正如您所看到的,堆空间和已用堆逐渐接近,直到 GP 升级并且服务器性能下降越来越多。
1.)当客户打电话给我时,我进行了堆转储
2.) 一半用户注销后(转储)
3.)所有用户注销后(转储)
用于比较:这是正常行为,正如预期的那样(注销后同一服务器,无需重新启动):
问题:
为什么 GC 尝试清理越来越多的数据,却没有增加堆大小? 如何进一步调试呢? 任何朝正确方向的推动都会有所帮助,并且非常感激。
我尝试解释 JFR 文件、HEAP 转储。但它并没有引导我走向任何特定的方向。问题不在于分配的对象,而在于 GC 的升级,并且 APP 不仅仅会占用更多的堆大小。
为什么 GC 尝试清理越来越多的数据,却没有增加堆大小?
GC 处于反复耗尽空间的模式。 每次发生这种情况时,都会触发 GC。 当您越来越接近抛出 OOME 的点时,GC 会变得越来越频繁。 (有一些方法可以避免这种 GC“死亡螺旋”行为......但这是另一个问题。)
您的堆已经达到 5GB ...您在 JVM 选项中设置的最大堆大小。 它无法增加堆大小。
如何进一步调试?
有两种可能性。
如果您(在您的应用程序中)存在内存泄漏,那么您需要找到并修复它。 有很多查找和修复 Java 内存泄漏的资源。
如果您没有内存泄漏,那么您要么需要增加最大堆大小,要么需要优化或调整应用程序以减少其堆使用量。
问题不在于分配的对象,而在于 GC 的升级,并且 APP 不仅仅会占用更多的堆大小。
除了增加 JVM 的
-Xmx
设置之外,没有简单的解决方案。
但是,您可以调整 GC 开销限制设置(请参阅调整 GC 开销超出参数)。 如果限制较低,则会导致 GC 抛出
OutOfMemoryError
earlier。 您可以安排 OOME 触发应用程序 JVM 的重新启动。 这可能比应用程序运行缓慢更容易被接受。