未终结的对象耗尽内存

Unfinalized objects exhausting memory

我们在 AWS Tomcat 中运行基于泽西岛 (1.x) 的服务在 ~20 个实例的数组中定期一个实例 "goes bad":结束在大约 4 小时的过程中,它的堆和 CPU 使用率增加,直到堆耗尽并且 CPU 被固定。那时它会自动从负载均衡器中删除并最终被杀死。

检查这些实例的堆转储,约 95% 的内存已被 java.lang.ref.Finalizer 的一个实例用完,该实例保存着各种东西,但大部分或全部与 HTTPS 相关连接 sun.net.www.protocol.https.HttpsURLConnectionImpl、sun.security.ssl.SSLSocketImpl、各种加密对象)。这些是我们使用 Jersey 的客户端库与外部网络服务建立的连接。来自 "healthy" 实例的堆转储不表示任何类型的问题。

在相对较低的负载实例下 运行 几天或几周都没有问题。随着负载的增加,实例故障的频率也会增加(平均 CPU 达到 ~40% 时每天发生几次)。

我们的 JVM 参数是:

-XX:+UseG1GC -XX:MaxPermSize=256m -Xmx1024m -Xms1024m

我正在为垃圾收集指标添加 JMX 日志记录,但我并不完全清楚我应该寻找什么。在这一点上,我主要是在寻找可能引发此类故障的想法或其他调查目标。

是否可能是连接泄漏?我假设你已经检查过了?

我遇到过类似的 GC 错误问题。根据您的 JVM 版本,您似乎正在使用实验性(并且可能存在错误)功能。您可以尝试禁用 G1 并使用默认的垃圾收集器。此外,根据您的版本,您可能 运行 陷入垃圾收集开销,它会放弃并且没有正确地收集 GC 内容,因为计算可以和不能丢弃的内容花费的时间太长。 -XX:-UseGCOverheadLimit 如果在您的 JVM 中可用,可能会有所帮助。

Java 使用单个终结器线程来清理死对象。您的机器的症状与堆积如山的最终确定一致。如果终结器线程速度减慢太多(因为某些对象需要很长时间才能完成),则终结器队列条目的累积可能会导致终结器线程越来越落后于传入的对象,直到一切都停止。

您可能会发现分析有助于确定哪些对象正在减慢终结器线程。

这最终证明是由 JVM 错误引起的(不幸的是,我已经将 link 丢失到我们追踪到的特定错误)。升级到更新版本的 OpenJDK(我们最终升级到 OpenJDK 1.7。0_50)解决了这个问题,而我们没有对代码进行任何更改。