堆转储中的Java内存引用不可能

4
我有一个在晚上7:41拍摄的Java Heap Dump,正在使用Eclipse Memory Analysis Tool进行分析。堆转储包括20个会话对象。
在我的堆中对其中一个会话对象使用Path to GC Roots命令显示了以下3个对该会话对象的引用:
1.来自Finalizer线程拥有的“未完成”链接列表的finalizer引用。我的对象是第三个要完成的对象。
2.从消息处理程序线程对会话对象的强引用,该线程本身被一个定时清理TimerTask引用,计划在晚上7:11运行。
3.从WeakHashMap $ Entry对会话对象的弱引用。WeakHashMap由静态强引用保持活动状态。
当会话对象仍具有强引用和弱引用时,它如何在终结器队列中?
在其余的19个会话对象中,还有1个在终结器队列中,具有类似的弱引用。其他18个会话对象仅具有弱引用。为什么GC没有清除这些弱引用?
一些一般性的观点:
1.只有在弱引用被清除后,对象才有资格进行终结(http://download.oracle.com/javase/6/docs/api/java/lang/ref/package-summary.html)
2.会话对象没有终结器可以使其复活,即使它有,也不能在对象仍在未完成队列中排在其他对象之后运行。
3.我的应用程序不使用幽灵引用,这是唯一能够在对象有资格进行终结后仍然存在的引用。即使我的应用程序使用幽灵引用,这些对象也不会公开它们所持有的对象的引用。

1
你是否曾经遇到过在可达状态下被终止的会话对象?如果没有,那么你不必担心。处于队列中并不一定意味着在强引用和/或弱引用仍然存在时就会被终止。 - finnw
什么机制会阻止终结器队列中的对象被终结?如果对象不符合条件,它们就不应该在此队列中。 - mchr
这在Cowan的回答中有解释。 - finnw
3个回答

11

我认为您在这里犯的错误是在这部分:

来自Finalizer线程拥有的“未完成”链接列表的终结器引用。我的对象是第三个要被终止的。

如果您指的是这个:

static private Finalizer unfinalized = null;
在Sun的Finalizer.java中(一个Finalizer包含一个nextprev Finalizer,因此这部分被称为“链表”,对于那些在家里玩的人来说),那不是要进行最终处理的事物列表。 Finalizer.add()并不像你假设的那样在对象不可达时进行终结处理过程时调用;相反,在对象创建时(例如在<init>期间通过本机代码为任何覆盖finalize()的对象调用该方法)调用该方法。 next链中的Finalizer的存在并不意味着它即将被终结处理;
static private ReferenceQueue queue

这个链表中保存了这样的对象。被放入链表中只是意味着它含有finalize()方法。

因此,你的第一个点是一个不相关的话题,而是第二个点使得该项对象仍然可达,并且第三个点源于第二个点(因为只要对象可达,WeakReference就不会被清除)。

希望这能帮到你!


即使对象本身没有错误的finalize,也可以通过其他引用将对象复活。 - bestsss

0

弱引用仅为GC提供指示。您无法保证何时会被清除。


假设垃圾收集器在某个时间点确定一个对象是弱可达的。此时,它将原子性地清除对该对象的所有弱引用。请参阅:http://download.oracle.com/javase/6/docs/api/java/lang/ref/WeakReference.html - mchr

0

您可以使一个已安排进行终结的对象再次可用。

我知道:

会话对象没有终结器,无法将其复活,即使有终结器,也不能在对象仍在未终结队列中排在其他对象之后时运行。

但是可能会有其他对象这样做(即具有对会话的引用并使自己被重新启动)。 无论哪种方式,都要显示该会话对象的终结器。

注意:在清除虚引用之前,对象将不可用于终结。 (虚引用最常用于调度临终前的清理操作,javadoc),前面的(而不是像弱引用一样后面的)。


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