Java - 匿名内部类的生命周期

10

当在属性更改监听器中使用匿名内部类时,类在对象生命周期的哪个时刻被垃圾回收?在包含类(SettingsNode)被释放之后吗?我是否应该在包含类(SettingsNode)的 finalizer 中显式地删除 PropertyChangeListener?

public class SettingsNode extends AbstractNode
{
    public SettingsNode(Project project, ProjectSettings projectSettings)
        throws IntrospectionException
    {   
        // use an anonymous inner class to listen for changes
        projectSettings.addPropertyChangeListener(ProjectSettings.PROP_NAME,
            new PropertyChangeListener()
            {
                @Override
                public void propertyChange(PropertyChangeEvent evt)
                {
                   // handle event
                }
            });
     }
}

根据提供的答案,匿名类似乎不是这种情况下的最佳选择。我现在认为实例内部类可能是更好的选择,因为我可以保持引用并在将来的某个时间点调用removePropertyListener方法。 - javacavaj
5个回答

7
与所有对象一样,当对匿名内部类的最后一个引用不再引用它时,它就有资格进行垃圾回收。我在这里使用了含糊其辞的措辞,因为Java不能保证事情会被垃圾回收 - 唯一的保证是只要有引用,就不会发生垃圾回收。
在这种特殊情况下,当projectSettings执行removePropertyListener()或者自身被垃圾回收时,就会出现这种情况。
由于projectSettings引用了匿名内部类,并且内部类引用了其包含类,这意味着包含类至少也会与内部类一样长寿。

尽管Java不能保证何时会进行垃圾回收,但如果另一种选择是OutOfMemoryError,我认为您必须说它保证了某些内容将被进行垃圾回收。 - Yishai
既然你已经说了,我就不用再说了 :) 当然,你是正确的,谢谢;但我希望我们的读者中没有人编写依赖于这种行为的代码。 - Carl Smotricz

2
您正在将您创建的PropertyChangeListener类添加到projectSettings对象中。只要projectSettings引用它,该PropertyChangeListener就不会被收集。

2
在你展示的例子中,无论是设置节点还是监听器,在项目设置被回收之前都无法被回收。你需要明确地移除该监听器,但最好寻找比终结器更可靠的地方。
SettingsNode 将在 PropertyChangeListener 被移除后才会被回收。使用匿名类作为监听器可能是内存泄漏的常见原因。
编辑:根据 Alex B 的问题,如果 projectSettings 存在于应用程序的生命周期中,则无法在注册后删除匿名监听器,因为在注册之后你没有对它的引用。随着多个 SettingsNode 实例的创建,它们将在构造函数中添加其监听器,但由于没有其他人对它们的引用,它们永远不会被移除。这将阻止 SettingsNodes 被移除,因为监听器具有对 SettingsNodes 的引用。

@Aaron:你能给出一个可能会发生内存泄漏的例子吗? - Alex B
@Alex:如果在不再需要SettingsNode时没有删除侦听器,那么问题中的代码实际上已经是内存泄漏的示例。请参见Carl的答案解释了这种情况的发生。 - x4u

2

这个问题很旧了。

然而,我不同意这里大部分回答的观点。

没有必要显式地删除监听器。在这种情况下,内部类PropertyChangeListener对象将一直存在,直到包含它的实例SettingsNode被垃圾回收。

你实际上无法删除PropertyChangeListener对象,因为没有为它保留引用。

虽然PropertyChangeListener对象确实引用其包含对象SettingsNode,但这并不能防止包含对象被取消引用和垃圾回收。

一旦包含对象被取消引用,SettingsNode包含的所有对象都成为“孤立的岛屿”。它们中的所有对象都将被垃圾回收。


0

内存泄漏的典型场景。不建议使用finalize,因为它可能会延迟GC。您可以公开清理函数或覆盖dispose和un-register。

真的很惊讶swing为什么没有内置的弱监听器注册。也许您可以尝试一些在source forge上的开源项目?


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