【问题标题】:Unfinalized objects exhausting memory未完成的对象耗尽内存
【发布时间】:2015-02-24 17:37:24
【问题描述】:

我们在 AWS 上的 Tomcat 中以大约 20 个实例的数组运行基于 Jersey (1.x) 的服务 一个实例周期性地“变坏”:在大约 4 小时的过程中,它的堆和 CPU 使用率会增加,直到堆已用尽,CPU 已固定。此时,它会自动从负载均衡器中删除并最终被杀死。

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

在负载相对较低的情况下,实例可以运行数天或数周而不会出现问题。随着负载的增加,实例故障的频率也会增加(平均 CPU 达到约 40% 时,每天发生几次)。

我们的 JVM 参数是:

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

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

【问题讨论】:

标签: java garbage-collection finalizer


【解决方案1】:

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

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

【讨论】:

  • 显然不是连接泄漏。健康实例的堆没有显示出任何额外连接的迹象;在不健康的情况下,它实际上只是终结器队列。我们使用的是 Java 7 (OpenJDK 1.7.0_25),所以 G1 得到官方支持,但我实际上不知道是什么让我们选择了那个。我们正在运行的其他服务似乎只是使用默认的 Parallel GC 没有问题。
【解决方案2】:

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

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

【讨论】:

  • 这听起来像是这里正在发生的事情,我只需要弄清楚是什么引起的。到目前为止,我在分析方面还没有取得很大成功,因为我无法在生产环境之外复制故障状态。当我查看一个健康的实例时,即使在高负载下也没有什么异常。
【解决方案3】:

这最终证明是由 JVM 错误引起的(不幸的是,我丢失了指向我们追踪到的特定错误的链接)。升级到更新版本的 OpenJDK(我们最终使用 OpenJDK 1.7.0_50)解决了这个问题,而无需我们对代码进行任何更改。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-08-23
    • 2012-11-10
    • 2012-08-19
    • 2011-02-09
    • 2015-10-01
    • 2018-12-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多