观察模式时会导致GC问题

时间:2009-11-11 16:35:35

标签: garbage-collection observer-pattern

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

所以在放弃一个观察者之前,它必须首先取消订阅。其他明智的,因为它仍然被主题引用,它永远不会被垃圾收集。

通常有3个解决方案:

  1. 手动取消订阅
  2. 弱参考。
  3. 它们都会引起其他问题。

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

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

    你怎么看?

3 个答案:

答案 0 :(得分:1)

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

不完全是你想要的,但有人必须决定“现在可以取消订阅”。当你的主题消失(但它应该已经杀死所有观察者)或者安装了观察者的对象时,就会发生这种情况。

如果您的应用意外终止,那么在这种情况下可能无法调用finalize()并不会造成伤害。

答案 1 :(得分:0)

如果你想删除一个观察者,你应该通过取消订阅通知发布者,否则它将尝试发送事件并根据它的编写方式,它可能会使应用程序崩溃,悄悄忽略错误或删除观察者。但是,如果你打开一些东西,请关闭它;如果您订阅,请取消订阅。

你不是取消订阅的事实是一个糟糕的设计,IMO。不要因为执行不力而责怪模式。

观察者模式运行良好,但如果您想减轻一些问题,可以使用AOP实现: http://www.cin.ufpe.br/~sugarloafplop/final_articles/20_ObserverAspects.pdf

答案 2 :(得分:0)

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

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

注意,顺便说一句:如果第一个对象持有对第二个对象的引用,当第一个对象的终结器运行时,后者将保证存在,但不能保证在任何特定的国家。除了取消订阅之外,清理线程不应该尝试对它做任何事情。