申请背景:
我们在 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“死亡螺旋”行为;请参见下文。)
在这种情况下,这是正常行为。
如何进一步调试?
有两种可能性。
如果您(在您的应用程序中)存在内存泄漏,那么您需要找到并修复它。 有很多查找和修复 Java 内存泄漏的资源。
如果您没有内存泄漏,那么您要么需要增加最大堆大小,要么需要优化或调整应用程序以减少其堆使用量。
问题不在于分配的对象,而在于 GC 的升级,并且 APP 不仅仅会占用更多的堆大小。
除了增加 JVM 的
-Xmx
设置之外,没有简单的解决方案。
但是,您可以调整 GC 开销限制(请参阅调整 GC 开销超出参数)。 如果限制较低,则会导致 GC 抛出
OutOfMemoryError
earlier。 您可以安排 OOME 触发应用程序 JVM 的重新启动。 这可能比应用程序运行缓慢更容易被接受。