最终版本占用了很多内存,4GB中有2.5GB被占用。

7
我阅读了许多关于finalizer工作原理的文章,这是我的理解:如果一个类实现了finalize方法,则JVM会在该对象上创建Finalizer实例作为看门狗。
当GC运行时,它会标记要处理的对象并将它们添加到引用队列中,然后finalizer线程将从队列中选择这些对象并执行它们的finalize方法。
我的问题是:如何找到堆转储中的对象,它们的finalize方法由于某种原因没有完成,并开始堆积引用队列?
引用队列是否有特定顺序?

3
想知道:你的终结器方法在做什么?为什么有这么多对象想使用一个方法,至少从理论上讲,没有人应该依赖它? - GhostCat
从源代码看,ReferenceQueue的工作方式类似于List——它包含Reference对象,每个Reference引用下一个Reference。也许第一个对象就是你需要的。也许你应该尝试在finalize方法上添加方面,并添加一些日志记录(比如调用xxx的Finalize; Finalize完成)——行中的最后一个条目将指向开始finalize但未完成finalize的对象。 - ekaerovets
我可能会选择另一种方式,对具有非平凡实现finalize()的类执行文本搜索 - 在您的代码库中有太多的候选项吗? - Hulk
@commit-man 说得好。我删除了错误的评论。我猜我混淆了这个,因为我刚刚从finalize中读到了这样的措辞:"在垃圾收集器确定没有更多引用该对象时,由垃圾收集器调用对象。" - Hulk
无论如何,JLS 中最重要的一句话可能是:“Java 编程语言没有指定 finalizer 会在多久之后被调用,除了说它会在对象的存储被重新使用之前发生。”只要没有尝试回收内存,就不能保证对象会被终结。 - Hulk
显示剩余2条评论
2个回答

0

是的,您可以从堆转储中获取对象。

首先,在 java.lang.ref.Finalizer.FinalizerThread#run 中,finalizer 引用将从队列中移除,然后在 runFinalizer 中,在方法 remove 的未完成引用的双向链接列表中也将其移除。

但您可以找到此引用,因为您知道它的 GC 根(它存在于 FinalizerThread 的堆栈上)

如何在 Eclipse MAT 中查找:

  1. 进入直方图并按 java.lang.ref.Finalizer 类过滤
  2. 右键单击包含 finalizers 的行-> 使用所有引用将最短路径合并到 gc 根中。
  3. 展开 FinalizerThread 行(通常应该包含一个对象 java.lang.Finalizer)
  4. 单击已展开的行,然后在检查器面板中单击属性选项卡,已完成的对象将是 referent enter image description here

谢谢回复。我没有看到检查器面板的选项。如何打开检查器面板? - Yashpal Bhandari
没关系。非常感谢你的帮助。我已经找到了选项。 - Yashpal Bhandari

0

这可能不是你想要的答案,但你考虑过使用PhantomReference而不是覆盖finalize()吗?这里有一篇文章讨论了这个问题。

基本思路是不建议依赖finalize()方法进行预死亡清理,因为:

  • 你无法预测它何时被调用。
  • 它会占用JVM的资源。
  • 它可能会阻止对象被垃圾回收。

PhantomReference提供了一种更清晰的方式来触发对象被垃圾回收器删除时的操作。

Object objectToHandle = new Object();
ReferenceQueue queue = new ReferenceQueue();
PhantomReference reference = new PhantomReference(objectToHandle, queue);

当垃圾回收器从内存中移除 objectToHandle 时,它的 reference 将会被添加到 queue 中。您可以通过调用 queue.remove() 来检测这一点,然后执行清理操作。

// will block until a reference becomes available
Reference removedRef = queue.remove();
//.. you can now perform clean-up actions

注意:PhantomReference.get() 总是返回 null,因此在对象已经从内存中删除后无法将其恢复。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接