java 听者作为弱引用的利弊

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6337760/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 15:27:05  来源:igfitidea点击:

Pros and Cons of Listeners as WeakReferences

javadesign-patternsobserver-patternweak-references

提问by pdeva

What are the pros and cons of keeping listeners as WeakReferences.

将听众保持为 WeakReferences 的利弊是什么。

The big 'Pro' of course is that:

最大的“专业”当然是:

Adding a listener as a WeakReference means the listener doesnt need to bother 'removing' itself.

添加一个监听器作为 WeakReference 意味着监听器不需要费心“删除”自己。

Update

更新

For those worried about the listener having the only reference to the object, why cant there be 2 methods, addListener() and addWeakRefListener()?

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

those who dont care about removal can use the latter.

那些不关心删除的人可以使用后者。

回答by BegemoT

First of all, using WeakReference in listeners lists will give your object different semantic, then using hard references. In hard-reference case addListener(...) means "notify supplied object about specific event(s) until I stop it explicitlywith removeListener(..)", in weak-reference case it means "notify supplied object about specific event(s) until this object will not be used by anybody else(or explicitly stop with removeListener)". Notice, it is perfectly legal in many situations to have object, listening for some events, and having no other references keeping it from GC. Logger can be an example.

首先,在侦听器列表中使用 Wea​​kReference 会给你的对象不同的语义,然后使用硬引用。在硬引用的情况下,addListener(...) 的意思是“通知提供的对象关于特定事件,直到我用 removeListener(..)明确地停止它”,在弱引用的情况下,它的意思是“通知提供的对象关于特定事件( s)直到该对象不会被其他任何人使用(或使用 removeListener 明确停止)”。请注意,在许多情况下,拥有对象、侦听某些事件并且没有其他引用使其远离 GC 是完全合法的。记录器就是一个例子。

As you can see, using WeakReference not just solve one problem ("I should keep in mind to not forget to remove added listener somewhere"), but also rise another -- "I should keep in mind that my listener can stop listen at any moment when there is no reference to it anymore". You not solve problem, you just trade one problem for another. Look, in any wayyou've forced to clearly define, design and trace livespan of you listener -- one way or another.

如您所见,使用 Wea​​kReference 不仅解决了一个问题(“我应该记住不要忘记在某处删除添加的侦听器”),而且还会引发另一个问题——“我应该记住,我的侦听器可以在任何时候停止侦听不再提及它的那一刻”。你不是解决问题,你只是用一个问题换另一个问题。看,以任何方式,您都被迫清楚地定义、设计和跟踪听众的寿命——一种或另一种方式。

So, personally, I agree with mention what use WeakReference in listeners lists is more like a hack than a solution. It's pattern worth to know about, sometimes it can help you -- to make legacy code work well, for example. But it is not pattern of choice :)

所以,就我个人而言,我同意提到在听众列表中使用 Wea​​kReference 更像是一种黑客而不是解决方案。这是值得了解的模式,有时它可以帮助您 - 例如,使遗留代码运行良好。但这不是选择模式:)

P.S. Also it should be noted what WeakReference introduce additional level of indirection, which, in some cases with extremely high event rates, can reduce performance.

PS 另外应该注意的是,WeakReference 引入了额外的间接级别,在某些情况下具有极高的事件率,这会降低性能。

回答by Kirk Woll

This is not a complete answer, but the very strength you cite can also be its principal weakness. Consider what would happen if action listeners were implemented weakly:

这不是一个完整的答案,但您引用的优势也可能是其主要弱点。考虑如果动作监听器的实现很弱会发生什么:

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

That action listener is going to get garbage collected at any moment! It's not uncommon that the only reference to an anonymous class is the event to which you are adding it.

该动作侦听器将随时收集垃圾!对匿名类的唯一引用是您将其添加到的事件,这并不少见。

回答by Jér?me Verstrynge

I have seen tons of code where listeners were not unregistered properly. This means they were still called unnecessarily to perform unnecessary tasks.

我见过大量代码,其中侦听器未正确取消注册。这意味着它们仍然被不必要地调用来执行不必要的任务。

If only one class is relying on a listener, then it is easy to clean, but what happens when 25 classes rely on it? It becomes much trickier to unregister them properly. The fact is, your code can start with one object referencing your listener and end up in a future version with 25 objects referencing that same listener.

如果只有一个类依赖一个监听器,那么清理起来很容易,但是当 25 个类依赖它时会发生什么?正确取消注册它们变得更加棘手。事实是,您的代码可以从一个对象引用您的侦听器开始,并在未来版本中以 25 个对象引用同一个侦听器结束。

Not using WeakReferenceis equivalent to taking a big risk of consuming unnecessary memory and CPU. It is more complicated, trickier and requires more work with hard references in the complex code.

不使用WeakReference就等于冒着消耗不必要的内存和CPU的很大风险。它更复杂、更棘手,并且需要在复杂代码中使用硬引用进行更多工作。

WeakReferencesare full of pros, because they are cleaned up automatically. The only con is that you must not forget to keep a hard reference elsewhere in your code. Typically, that would in objects relying on this listener.

WeakReferences充满了优点,因为它们会自动清理。唯一的缺点是您不能忘记在代码的其他地方保留硬引用。通常,这将在依赖此侦听器的对象中进行。

I hate code creating anonymous class instances of listeners (as mentioned by Kirk Woll), because once registered, you can't unregister these listeners anymore. You don't have a reference to them. It is really bad coding IMHO.

我讨厌代码创建侦听器的匿名类实例(如 Kirk Woll 所述),因为一旦注册,您就无法再取消注册这些侦听器。你没有参考他们。恕我直言,编码真的很糟糕。

You can also nulla reference to a listener when you don't need it anymore. You don't need to worry about it anymore.

null当您不再需要时,您还可以引用侦听器。你不必再担心了。

回答by James Scriven

There are really no pros. A weakrefrence is usually used for "optional" data, such as a cache where you don't want to prevent garbage collection. You don't want your listener garbage collected, you want it to keep listening.

真的没有什么优点。弱引用通常用于“可选”数据,例如您不想阻止垃圾收集的缓存。你不希望你的听众垃圾收集,你希望它继续听。

Update:

更新:

Ok, I think I might have figured out what you are getting at. If you are adding short-lived listeners to long-lived objects there may be benefit in using a weakReference. So for example, if you were adding PropertyChangeListeners to your domain objects to update the state of the GUI that is constantly being recreated, the domain objects are going to hold on to the GUIs, which could build up. Think of a big popup dialog that is constantly being recreated, with a listener reference back to an Employee object via a PropertyChangeListener. Correct me if I'm wrong, but I don't think the whole PropertyChangeListener pattern is very popular anymore.

好吧,我想我可能已经弄清楚你在说什么了。如果您向长期存在的对象添加短期存在的侦听器,则使用weakReference 可能会有好处。因此,例如,如果您将 PropertyChangeListeners 添加到域对象以更新不断重新创建的 GUI 的状态,域对象将保留可能建立的 GUI。想想一个不断重新创建的大弹出对话框,其中侦听器通过 PropertyChangeListener 引用回 Employee 对象。如果我错了,请纠正我,但我认为整个 PropertyChangeListener 模式不再流行了。

On the other hand, if you are talking about listeners between GUI elements or having domain objects listening to GUI elements, you won't be buying anything, since when the GUI goes away, so will the listeners.

另一方面,如果您在谈论 GUI 元素之间的侦听器或让域对象侦听 GUI 元素,您将不会购买任何东西,因为当 GUI 消失时,侦听器也将消失。

Here are a couple interesting reads:

这里有一些有趣的读物:

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

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

How to resolve swing listener memory leaks?

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

回答by Nicolas Bousquet

To be honest I don't really buy that idea and exactly what you expect to do with a addWeakListener. Maybe it is just me, but it appear to be a wrong good idea. At first it is seducing but the problems it might implies are not negligible.

老实说,我并不真正相信这个想法,也不完全相信您希望使用 addWeakListener 做什么。也许这只是我,但这似乎是一个错误的好主意。起初它很诱人,但它可能暗示的问题不容忽视。

With weakReference you are not sure that the listener will no longer be called when the listener itself is no longer referenced. The garbage collector can free up menmory a few ms later or never. This mean that it might continue to consume CPU and make strange this like throwing exception because the listener shall not be called.

使用weakReference,您不确定当不再引用侦听器本身时将不再调用侦听器。垃圾收集器可以在几毫秒后或永远不会释放内存。这意味着它可能会继续消耗 CPU 并像抛出异常一样奇怪,因为不应调用侦听器。

An example with swing would be to try to do things you can only do if your UI component is actually attached to an active window. This could throw an exception, and affect the notifier making it to crash and preventing valid listeners to be notofied.

Swing 的一个例子是尝试做只有当 UI 组件实际附加到活动窗口时才能做的事情。这可能会引发异常,并影响通知程序使其崩溃并阻止通知有效侦听器。

Second problem as already stated is anonymous listener, they could be freed too soon never notified at all or only a few times.

如前所述,第二个问题是匿名侦听器,他们可能会过早地被释放,根本不会通知或只通知几次。

What you are trying to achieve is dangerous as you cannot control anymore when you stop receiving notifications. They may last for ever or stop too soon.

您试图实现的目标很危险,因为当您停止接收通知时,您将无法再控制。它们可能会持续到永远,也可能停止得太早。

回答by Deepak

Because you are adding WeakReference listener, I'm assuming, you are using a custom Observable object.

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

It makes perfect sense to use a WeakReference to an object in the following situation. - There is a list of listeners in Observable object. - You already have a hard reference to the listeners somewhere else. (you'd have to be sure of this) - You don't want the garbage collector to stop clearing the listeners just because there is a reference to it in the Observable. - During garbage collection the listeners will be cleared up. In the method where you notify the listeners, you clear up the WeakReference objects from the notification list.

在以下情况下对对象使用 Wea​​kReference 是非常有意义的。- Observable 对象中有一个监听器列表。- 您已经在其他地方对听众进行了严格的参考。(您必须确定这一点) - 您不希望垃圾收集器仅仅因为 Observable 中有对它的引用而停止清除侦听器。- 在垃圾收集期间,侦听器将被清除。在通知侦听器的方法中,从通知列表中清除 WeakReference 对象。

回答by Christian

In my opinion it's a good idea in most cases. The code that is responsible for releasing the listener is at the same place where it gets registered.

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

In practice i see a lot of software which is keeping listeners forever. Often programmers are not even aware that they should unregister them.

在实践中,我看到很多软件可以让听众永远保持下去。通常程序员甚至不知道他们应该取消注册。

It usually is possible to return a custom object with a reference to the listener that allows manipulation of when to unregister. For example:

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

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

this code would register the listener, return an object that encapsulates the listener and has a method, keepFor that adds the listener to a static weakHashMap with the instance parameter as the key. That would guarantee that the listener is registered at least as long as someInstance and otherInstance are not garbage collected.

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

There can be other methods like keepForever() or keepUntilCalled(5) or keepUntil(DateTime.now().plusSeconds(5)) or unregisterNow().

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

Default can be keep forever (until unregistered).

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

This could also be implemented without weak references but phantom references that trigger the removal of the listener.

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

edit: created a small lib which implements a basic version of this aproach https://github.com/creichlin/struwwel

编辑:创建了一个小库,它实现了这个方法的基本版本https://github.com/creichlin/struwwel

回答by jkraybill

I can't think of any legitimate use case for using WeakReferences for listeners, unless somehow your use case involves listeners that explicitly shouldn't exist after the next GC cycle (that use case, of course, would be VM/platform specific).

我想不出任何将 WeakReferences 用于侦听器的合法用例,除非您的用例以某种方式涉及在下一个 GC 周期后明确不存在的侦听器(当然,该用例将特定于 VM/平台)。

It's possible to envision a slightly more legitimate use case for SoftReferences, where the listeners are optional, but take up a lot of heap and should be the first to go when free heap size starts getting dicey. Some sort of optional caching or other type of assisting listener, I suppose, could be a candidate. Even then it seems like you'd want the internals of the listeners to utilize the SoftReferences, not the link between the listener and listenee.

可以为 SoftReferences 设想一个更合理的用例,其中侦听器是可选的,但占用大量堆,并且在空闲堆大小开始变得危险时应该是第一个。我想,某种可选的缓存或其他类型的辅助侦听器可能是候选对象。即便如此,您似乎还是希望听者的内部使用 SoftReferences,而不是听者和被听者之间的链接。

Generally if you're using a persistent listener pattern, though, the listeners are non-optional, so asking this question may be a symptom that you need to reconsider your architecture.

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

Is this an academic question, or do you have a practical situation you're trying to address? If it's a practical situation I'd love to hear what it is -- and you could probably get more, less abstract advice on how to solve it.

这是一个学术问题,还是您要解决的实际情况?如果这是一个实际情况,我很想听听它是什么——你可能会得到更多关于如何解决它的抽象建议。

回答by Teto

I have 3 suggestions for the original poster. Sorry for resurrecting an old thread but I think my solutions were not previously discussed in this thread.

我对原始海报有 3 条建议。很抱歉复活了一个旧线程,但我认为我的解决方案之前没有在这个线程中讨论过。

First, Consider following the example of javafx.beans.values.WeakChangeListener in the JavaFX libraries.

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

Second, I one upped the JavaFX pattern by modifying the addListener methods of my Observable. The new addListener() method now creates instances of the corresponding WeakXxxListener classes for me.

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

The "fire event" method was easily modified to dereference the XxxWeakListeners and to remove them when the WeakReference.get() returned null.

“火灾事件”方法很容易修改为取消引用 XxxWeakListeners 并在 WeakReference.get() 返回 null 时删除它们。

The remove method was now a bit nastier since I need to iterate the entire list, and that means I need to do synchronization.

remove 方法现在有点麻烦,因为我需要迭代整个列表,这意味着我需要进行同步。

Third, Prior to implementing this strategy I employed a different method which you may find useful. The (hard reference) listeners got a new event they did a reality check of whether or not they were still being used. If not, then they unsubscribed from the observer which allowed them to be GCed. For short lived Listeners subscribed to long lived Observables, detecting obsolescence was fairly easy.

第三,在实施此策略之前,我采用了一种您可能会发现有用的不同方法。(硬引用)侦听器收到一个新事件,他们对它们是否仍在使用进行了现实检查。如果没有,那么他们取消订阅观察者,这允许他们被 GC。对于订阅了长期 Observables 的短期听众来说,检测过时是相当容易的。

In deference to the folks who stipulated that it was "good programming practice to always unsubscribe your listeners, whenever a Listener resorted to unsubscribing itself, I made sure to create a log entry and corrected the problem in my code later.

尊重那些规定“始终取消订阅侦听器是一种良好的编程习惯,每当侦听器自行取消订阅时,我确保创建一个日志条目并在以后更正我的代码中的问题。

回答by mdma

WeakListeners are useful in situations where you specifically want GC to control the lifetime of the listener.

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

As stated before, this really is different semantics, compared to the usual addListener/removeListener case, but it is valid in some scenarios.

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

For example, consider a very large tree, which is sparse - some levels of nodes are not explicitly defined, but can be inferred from parent nodes further up the hierarchy. The implicitly defined nodes listen to those parent nodes that are defined so they keep their implied/inherited value up to date. But, the tree is huge - we don't want implied nodes to be around forever - just as long as they are used by the calling code, plus perhaps a LRU cache of a few seconds to avoid churning the same values over and over.

例如,考虑一个非常大的树,它是稀疏的 - 某些级别的节点没有明确定义,但可以从层次结构更上一层的父节点推断出来。隐式定义的节点侦听那些定义的父节点,以便它们保持其隐含/继承的值是最新的。但是,树是巨大的——我们不希望隐含的节点永远存在——只要它们被调用代码使用,再加上几秒钟的 LRU 缓存,以避免一遍又一遍地搅动相同的值。

Here, the weak listener makes it possible for child nodes to listen to parents while also having their lifetime decided by reachability/caching so the structure doesn't maintain all the implied nodes in memory.

在这里,弱侦听器使子节点可以侦听父节点,同时它们的生命周期也由可达性/缓存决定,因此该结构不会在内存中维护所有隐含节点。