【问题标题】:Why GC doesn't collect my objects?为什么 GC 不收集我的对象?
【发布时间】:2012-11-10 02:38:01
【问题描述】:

我有一个 java 程序,它不断调用java.util.zip 来压缩/解压缩数据。它会在几秒钟内耗尽内存。 我使用jmap 进行了内存转储,我正在使用jhat 查看它。

终结器摘要显示Total instances pending finalization: 0。如果我理解正确,我没有任何对象 (1) 具有 finalize() 方法,(2) 已被 GC 标记并且 (3) 正在等待最终确定。这看起来不错。

当我查看特定对象时,对该对象的唯一引用是java.lang.ref.Finalizer。 Finalizer 对象是为每个具有 finalize() 方法的对象创建的,无论该对象是否被 GC 处理。所以看起来没有什么能阻止这个 Deflater 对象被 GC 处理。

对象位于 0x7f4aeb7a35d0

java.util.zip.Deflater@0x7f4aeb7a35d0 的实例(51 字节)

对该对象的引用:

java.lang.ref.Finalizer@0x7f4aeb8607c8(64 字节):字段引用

程序在运行中被System.in.read() 暂停。一段时间后内存使用量不会下降。

更新:

我应该说清楚。内存转储显示许多对象没有经过 GC,但没有其他对象(终结器对象除外)引用它们。我正在尝试找出为什么它们没有被 GC 处理。

【问题讨论】:

  • @dashrb 我的代码中没有使用 Deflater。我正在使用使用 Deflater 的 Apache Thrift。 Deflater 不在范围内,因为 Show all members of the rootset 仅显示 1 个对 Deflater 类的引用,但没有实例。
  • 实际上,您的内存不足并且没有被 GC 标记的对象是不好的。这意味着您的代码(或您使用的某些库代码)保留了对对象的引用,因此 GC 无法收集它们。
  • @TedHopp 这就是我要找出的。我不知道如何找到这些引用,因为内存转储显示没有任何引用。
  • 如果您使用 jmap 打印对象分配的直方图,这可能会显示哪种对象正在占用内存并为您提供下一步查看的线索。有一篇很好的文章 here 关于跟踪内存泄漏的工具。
  • 为什么不使用内存配置文件来查看到底什么是活动的以及谁在引用谁?

标签: java memory-leaks garbage-collection jmap jhat


【解决方案1】:

您确定关闭了流并在 deflater 上调用了end?如果这是一个您已经尝试过的简单建议,我很抱歉,但是有很多关于在使用 Deflater 时没有立即调用 end 时出现内存泄漏的投诉,例如:

根本原因显然是当涉及到本机元素使用的内存时,收集器无法跟上应用程序的速度。这也解释了您在分析时看到的行为:内存已准备好被回收,但回收的速度不够快。

由于您写的不是直接使用Deflator,而是通过 Apache Thrift,请尝试确定该库中的哪个方法负责结束 deflator,并确保您调用了该方法。

【讨论】:

    【解决方案2】:

    基本问题是您没有从 C 堆中释放对象。 java.util.zip 中的许多类使用Deflater。 Deflater 维护对 C 堆中数据的引用。 您的代码可能没有在ZipOutputStream 或DeflaterOutputStream 上调用close(),或者它没有在Deflater 上调用end()。

    (如果您将自己的Deflater 传递给ZipOutputStream 或DeflaterOutputStream,您有责任在Deflater 上调用end()。)

    GC 在您的情况下的帮助有限,因为必须取消引用并最终确定流。这可能需要多次 GC 扫描。我experienced Jetty 有类似的问题,并提出了解决方案。

    【讨论】:

      【解决方案3】:

      也许你应该为你的对象使用 Wea​​kReference。这样 GC 可以很快地完成()你的对象

      http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/ref/WeakReference.html http://weblogs.java.net/blog/2006/05/04/understanding-weak-references

      【讨论】:

        【解决方案4】:

        添加到 Marian-Daniel Craciunescu 的答案

        我在使用另一种类型时确实遇到了这种问题。

        我找到的解决方案如下:

        • 将对象放入地图或您可以轻松删除其引用的地方
        • 将也对象放入 WeakReference 中以供使用
        • 照常使用您的参考资料
        • 从地图中删除不再使用的参考

        注意继承的引用

        很有可能您的对象在另一个对象中被“隐藏-内部引用”,请使用调试器并使用它“扩展”所有对象。

        你永远不会知道什么时候 GC 正在运行,也不会它将处理什么(除了正在使用的对象)。

        有用的链接

        您应该阅读(如果尚未完成)这篇关于 WeakReferences 的帖子:Understanding Java's Reference classes: SoftReference, WeakReference, and PhantomReference

        【讨论】:

          【解决方案5】:

          首先在考虑内存泄漏之前,我们应该尝试了解内存需求对于您正在执行的处理是否正常。 JVM 可用的内存大小是多少,即 -XmX。如果增加内存大小或减小正在处理的文件大小是否有效?

          【讨论】:

            猜你喜欢
            • 2012-01-31
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-04-21
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多