当观察模式导致 GC 问题时

发布于 2024-08-11 01:15:54 字数 280 浏览 5 评论 0原文

在支持GC的语言中,当观察者订阅主题的事件时,实际上主题获得了观察者的引用。

因此,在删除观察者之前,必须先取消订阅。否则,因为它仍然被主题引用,所以它永远不会被垃圾收集。

通常有3种解决方案:

  1. 手动取消
  2. 订阅弱引用。

它们都会导致其他问题。

所以通常我不喜欢使用观察者模式,但我仍然找不到任何替代品。

我的意思是,这种模式以如此自然的方式描述事物,你几乎找不到更好的了。

你对此有何看法?

In a GC enabled language, when observer subscribes to events of subject, actually subject got a reference of observer.

So before drop an observer, it must un-subscribes first. Other wise, because it's still referenced by subject, it will never be garbage collected.

Normally there are 3 solutions:

  1. Manually un-subscribes
  2. Weak Reference.

Both of them cause other problems.

So usually I don't like to use observer patterns, but I still can not find any replacement for that.

I mean, this pattern describes thing in such a natural way that You could hardly find anything better.

What do you think about it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

深爱不及久伴 2024-08-18 01:15:54

在这种情况下,您可以在 Java 中使用 finalize()。当您必须释放资源(如数据库连接)时,finalize() 是一个坏主意,因为某些外部系统会受到影响。在您的情况下,安装观察者的对象将在您的应用程序运行时被GC,然后,finalize() 将被调用,并且它可以取消订阅观察者。

不完全是你想要的,但必须有人决定“现在可以取消订阅”。这要么发生在你的主体消失时(但它应该已经杀死了所有观察者),要么发生在安装了观察者的物体上。

如果您的应用程序意外终止,那么在这种情况下可能不会调用 finalize() 也没什么坏处。

In this scenario, you can use finalize() in Java. finalize() is a bad idea when you have to release a resource (like a DB connection) because some outside system is affected. In your case, the object which installed the observer will be GC'd during the runtime of your app and then, finalize() will be called and it can unsubscribe the observer.

Not exactly what you want but someone must decide "it's okay to unsubscribe, now". That either happens when your subject goes away (but it should already kill all observers) or the object which installed the observer.

If your app terminates unexpectedly, well, it doesn't hurt that finalize() might not be called in this case.

沉睡月亮 2024-08-18 01:15:54

如果您想删除观察者,您应该首先通过取消订阅来通知发布者,否则它将尝试发送事件,并且根据事件的编写方式,它可能会使应用程序崩溃,静静地忽略错误或删除观察者。但是,如果您打开某些内容,请将其关闭;如果您订阅,请取消订阅。

在我看来,你不取消订阅的事实是一个糟糕的设计。不要将实施不佳归咎于该模式。

观察者模式效果很好,但如果你想缓解一些问题,你可以使用 AOP 来实现:
http://www.cin.ufpe.br/~sugarloafplop/final_articles/ 20_ObserverAspects.pdf

If you want to remove an observer you should inform the publisher by unsubscribing, first, otherwise it will try to send out events and depending on how it is written, it could crash the app, quietly ignore the error or remove the observer. But, if you open something, close it; if you subscribe, unsubscribe.

The fact that you are not unsubscribing is a bad design, IMO. Don't blame the pattern for a poor implementation.

The observer pattern works well, but if you want to alleviate some of the issues, you could use AOP for the implementation:
http://www.cin.ufpe.br/~sugarloafplop/final_articles/20_ObserverAspects.pdf

吹泡泡o 2024-08-18 01:15:54

考虑一个对象的场景,该对象计算某些可观察事物发生变化的频率。对对象的引用有两种类型:(1) 对计数感兴趣的实体的引用; (2) 由可观察事物使用的那些,它们对计数并不真正感兴趣,但需要更新它。对计数感兴趣的实体应该持有对对象的引用,该对象又持有对管理计数的对象的引用。必须更新计数但对此并不真正感兴趣的实体应该只保存对第二个对象的引用。

如果第一个对象拥有终结器,则当该对象超出范围时它将被触发。这可能会触发第二个对象取消订阅,但可能不应该直接取消订阅。取消订阅可能需要获取锁,并且终结器不应该等待锁。相反,第一个对象的终结器可能应该将该对象添加到使用 Interlocked.CompareExchange 维护的链接列表中,并且其他一些线程应该定期轮询该列表以查找需要取消订阅的对象。

请注意,顺便说一句:如果第一个对象持有对第二个对象的引用,则当第一个对象的终结器运行时,将保证后者存在,但不能保证它处于任何特定状态。除了取消订阅之外,清理线程不应尝试对其执行任何操作。

Consider the scenario of an object which counts how often some observable thing changes. There are two types of references to the object: (1) those by entities which are interested in the count; (2) those used by the observable thing(s) which aren't really interested in the the count, but need to update it. The entities which are interested in the count should hold a reference to an object which in turn holds a reference to the one that manages the count. The entities that will have to update the count but aren't really interested in it should just hold references to the second object.

If the first object holds a finalizer, it will be fired when the object goes out of scope. That could trigger the second object to unsubscribe, but it should probably not be unsubscribed directly. Unsubscription would probably require acquiring a lock, and finalizers should not wait on locks. Instead, the finalizer of the first object should probably add that object to a linked list maintained using Interlocked.CompareExchange, and some other thread should periodically poll that list for objects needing unsubscription.

Note, btw: If the first object holds a reference to second object, the latter would be guaranteed to exist when the finalizer for the first object runs, but it would not be guaranteed to be in any particular state. The cleanup thread should not try to do anything with it other than unsubscribe.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文