作为弱引用的听众的优点和缺点

把听众保持为WeakReferences有什么优点和缺点?

大的“专业”当然是这样的:

将侦听器添加为WeakReference意味着侦听器不需要打扰“删除”本身。

更新

对于那些担心只有引用该对象的监听者,为什么不能有2个方法,addListener()和addWeakRefListener()?

那些不在意拆卸的人可以使用后者。

首先,在侦听器列表中使用WeakReference将使您的对象具有不同的语义 ,然后使用硬引用。 在硬引用的情况下,addListener(…)的意思是“通知提供的对象关于特定事件, 直到我明确用removeListener(..)”来停止它 ,在弱引用的情况下,它意味着“通知提供的对象关于特定事件s), 直到这个对象不会被其他人使用 (或者用removeListener明确地停止)“。 注意,在许多情况下,拥有对象,倾听某些事件,没有其他引用使其与GC保持一致,这是完全合法的。 logging器可以是一个例子。

正如你所看到的,使用WeakReference不只是解决了一个问题(“我应该记住不要忘记删除额外的听众”),而且还会升起另一个问题 – “我应该记住,我的听众可以停止听任何那时候再也没有提到它了“。 你没有解决问题,你只是交换一个问题。 不pipe怎样,你都必须明确地定义,devise和追踪听众的活跃程度。

所以,我个人同意在听众列表中使用WeakReference更像是一种解决scheme。 这是值得了解的模式,有时它可以帮助你 – 例如,使遗留代码很好地工作。 但它不是select的模式:)

PS另外,应该注意的是,WeakReference引入了更多的间接性,在某些情况下,事件发生率极高,可能会降低性能。

这不是一个完整的答案,但你引用的力量也可能是它的主要弱点。 考虑一下如果行为监听者被弱化执行会发生什么:

 button.addActionListener(new ActionListener() { // blah }); 

那个动作监听者随时都会收集垃圾! 一个匿名类的唯一引用是你添加它的事件并不罕见。

我已经看到吨代码听众没有正确注册。 这意味着他们仍然不必要地被称为执行不必要的任务。

如果只有一个class级依赖于一个听众,那么很容易清理,但是25个class级依靠这个class级会发生什么? 正确地取消注册变得更加棘手。 事实是,你的代码可以从一个引用你的监听器的对象开始,最终在未来的版本中引用同一个监听器。

不使用WeakReference相当于消耗不必要的内存和CPU的风险。 这是更复杂,更复杂,需要更多的工作与艰苦的参考复杂的代码。

WeakReferences充满了优点,因为它们被自动清理。 唯一的缺点就是你不能忘记在代码的其他地方保留一个硬引用。 通常情况下,这将依赖于这个监听器的对象。

我讨厌创build监听器的匿名类实例的代码(正如Kirk Woll所提到的),因为一旦注册了,就不能注销这些监听器了。 你没有提及他们。 编码恕我直言,真是不好。

当你不再需要的时候,你也可以将一个引用给一个监听器。 你不用担心它了。

真的没有优点。 weakrefrence通常用于“可选”数据,例如您不想防止垃圾回收的caching。 你不希望你的听众垃圾收集,你希望它保持倾听。

更新:

好吧,我想我可能已经知道你在做什么了。 如果您将短命的侦听器添加到长寿命的对象中,那么使用weakReference可能会有好处。 因此,例如,如果将PropertyChangeListeners添加到域对象以更新不断重新创build的GUI的状态,那么域对象将保留在可能build立的GUI上。 想象一个不断被重新创build的大型popup式对话框,监听器通过一个PropertyChangeListener返回一个Employee对象。 纠正我,如果我错了,但我不认为整个PropertyChangeListener模式是非常受欢迎了。

另一方面,如果您正在讨论graphics用户界面元素之间的监听器,或者让域对象监听GUI元素,那么您将不会购买任何东西,因为当GUI消失的时候,监听器也会消失。

这里有一些有趣的内容:

http://www.javalobby.org/java/forums/t19468.html

如何解决摆动监听器内存泄漏?

说实话,我并不真正购买这个想法,确切地说,你期望用addWeakListener做什么。 也许这只是我,但它似乎是一个错误的好主意。 起初它是诱惑,但它可能暗示的问题不可忽略。

使用weakReference时,您不确定在侦听器本身不再被引用时不再调用侦听器。 垃圾收集器可以在几毫秒之后或从不释放存储器。 这意味着它可能会继续消耗CPU并使得像抛出exception这样的奇怪,因为不应该调用监听器。

Swing的一个例子就是尝试去做一些事情,如果你的UI组件实际上被连接到了一个活动窗口上,你就只能这样做。 这可能会引发exception,并影响通知程序使其崩溃,并阻止有效的侦听器不通知。

第二个问题,如已经提到的,是匿名的听众,他们可能被释放得太早,从来没有通知或只有几次。

你试图达到的是危险的,因为当你停止接收通知时你无法控制。 他们可能会永远持续下去,或者过早停下来。

因为您正在添加WeakReference侦听器,所以我假定您正在使用自定义的Observable对象。

在以下情况下使用WeakReference指向对象是非常有意义的。 – Observable对象中有一个监听器的列表。 – 你已经有一个很强的参考,在其他地方的听众。 (你必须确定这一点) – 你不希望垃圾回收器停止清除监听器只是因为在Observable中有一个引用。 – 垃圾收集期间,侦听器将被清除。 在通知监听器的方法中,清除通知列表中的WeakReference对象。

我想不出有什么合法的用例来为侦听器使用WeakReferences,除非你的用例涉及在下一个GC周期(当然这个用例是VM /平台特定的)之后明确不应该存在的侦听器。

可以设想一个SoftReferences稍微更合理的用例,其中听众是可选的,但占用大量的堆,应该是第一个去免费堆大小开始越来越dicey。 我猜想,某种可选的caching或其他types的辅助监听者可能是候选人。 即使这样,您似乎希望听众的内部使用SoftReference,而不是监听者和监听者之间的链接。

通常,如果您使用的是持久性侦听器模式,那么侦听器是非可选的,因此,提出这个问题可能是您需要重新考虑架构的一个症状。

这是一个学术问题,还是你有一个实际的情况,你试图解决? 如果这是一个实际的情况,我很想听听它是什么样的 – 而且你可能会得到更多,更less的关于如何解决这个问题的抽象build议。

在我看来,在大多数情况下这是个好主意。 负责释放侦听器的代码与注册的地方相同。

在实践中,我看到很多软件永远留着听众。 程序员经常不知道他们应该取消注册。

通常可以返回一个自定义的对象,引用监听器,允许操作何时取消注册。 例如:

 listeners.on("change", new Runnable() { public void run() { System.out.println("hello!"); } }).keepFor(someInstance).keepFor(otherInstance); 

此代码将注册侦听器,返回封装侦听器的对象并具有方法,keepFor将侦听器添加到以实例参数为关键字的静态weakHashMap中。 这将保证监听器至less被注册,只要someInstance和otherInstance不被垃圾收集。

可以有其他方法,如keepForever()或keepUntilCalled(5)或keepUntil(DateTime.now()。plusSeconds(5))或unregisterNow()。

默认可以永久保存(直到未注册)。

这也可以在没有弱引用的情况下实现,但是可以触发删除侦听器的幻影引用。

编辑:创build一个小的lib,实现这个aproach的基本版本https://github.com/creichlin/struwwel

WeakListeners在您特别希望GC控制侦听器的生命周期的情况下非常有用。

如前所述,与通常的addListener / removeListener大小写相比,这实际上是不同的语义,但在某些情况下它是有效的。

例如,考虑一个非常大的树,它是稀疏的 – 一些级别的节点没有明确定义,但是可以从父级节点进一步在层次结构中推断出来。 隐式定义的节点监听那些被定义的父节点,以保持其隐含/inheritance的值是最新的。 但是,树是巨大的 – 我们不希望隐含的节点永远在 – 只要它们被调用代码使用,加上也许几秒钟的LRUcaching,以避免搅动相同的值一遍又一遍。

在这里,弱听者使得子节点可以收听父母,同时通过可达性/高速caching来确定他们的生命期,所以结构不会将所有隐含的节点保存在内存中。

您可能还需要使用WeakReference来实现您的监听器,如果您将其注销到某个不保证每次都被调用的地方。

我似乎还记得我们在我们的ListView中的行视图中使用的一个自定义PropertyChangeSupport监听器有一些问题。 我们找不到一个好的可靠的方法来取消注册这些监听器,所以使用WeakReference监听器似乎是最干净的解决scheme。

从testing程序看来,匿名ActionListeners不会阻止对象被垃圾收集:

 import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; public class ListenerGC { private static ActionListener al = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { System.err.println("blah blah"); } }; public static void main(String[] args) throws InterruptedException { { NoisyButton sec = new NoisyButton("second"); sec.addActionListener(al); new NoisyButton("first"); //sec.removeActionListener(al); sec = null; } System.out.println("start collect"); System.gc( ); System.out.println("end collect"); Thread.sleep(1000); System.out.println("end program"); } private static class NoisyButton extends JButton { private static final long serialVersionUID = 1L; private final String name; public NoisyButton(String name) { super(); this.name = name; } @Override protected void finalize() throws Throwable { System.out.println(name + " finalized"); super.finalize(); } } } 

生产:

 start collect end collect first finalized second finalized end program 

我有3个build议的原始海报。 对不起复活旧的线程,但我认为我的解决scheme之前没有讨论过这个线程。

首先,请考虑JavaFX库中的javafx.beans.values.WeakChangeListener示例。

其次,我通过修改Observable的addListener方法来boostJavaFX模式。 新的addListener()方法现在为我创build相应的WeakXxxListener类的实例。

WeakReference.get()返回null时,“fire事件”方法很容易被修改以解引用XxxWeakListeners并将其删除。

删除方法现在有点难看,因为我需要遍历整个列表,这意味着我需要做同步。

第三,在实施这个战略之前,我采用了一种你可能会觉得有用的方法。 (参考)听众得到一个新的事件,他们确实检查他们是否仍在使用。 如果不是的话,那么他们退订了允许他们进入GC的观察者。 对于短期听众订阅长期观察,检测过时是相当容易的。

为了尊重那些规定“总是取消订阅你的听众的良好编程习惯”的人们,每当Listener采取解除订阅的方式时,我就确保创build一个日志条目并在之后的代码中纠正这个问题。