垃圾回收器和事件处理程序

8

一个快速的问题。假设我有一个如下示例所示的类实现。

class Subscriber
{
    private Publisher publisher = new Publisher;


    public Subscriber()
    {
       publisher.SomeEvent += new EventHandler(OnEventFired);
    }

    private void OnEventFired(object sender, EventArgs e)
    {
    }

}

在程序中,我有一个像这样的方法:

public void DoSomething()
{
    Subscriber subscriber = new Subscriber();
}

我是否可以期望这会导致内存泄漏,因为订阅者从未取消订阅发布者的事件,从而导致它们彼此保持强引用?

2个回答

17

这不会导致泄漏 - 垃圾回收器可以处理循环引用而没有任何问题。

然而,这意味着发布者实际上会引用订阅者,所以订阅者无法被垃圾回收,直到发布者有资格进行垃圾回收或取消订阅事件为止。


2
嗯...我有点困惑了。你说当发布者被收集时,订阅者才会被收集,但在我的情况下,发布者是订阅者的实例变量,只有当订阅者被收集时,它才会被收集。然而与此同时,你又说GC应该可以处理循环引用而没有问题。我是否漏掉了什么,还是你的陈述存在矛盾? - L.E.O
5
@L.E.O.: 没有矛盾。如果没有其他内容与这两个对象相关,它们都将符合收集条件。如果您保留对“订阅者(Subscriber)”的引用,则由于实例变量,它将保持“发布者(Publisher)”活动状态。如果您在其他地方保留了对“发布者”的引用,那么由于事件订阅,它将使“订阅者”保持活动状态。 - Jon Skeet
好的,现在我明白你想说什么了。谢谢。 - L.E.O

2
如果在事件发布者的GC生命周期内,可能会出现大量的事件订阅者被创建和放弃而未被取消订阅,这些悬空的订阅将构成内存泄漏。如果事件发布者在订阅者被放弃时即将变为垃圾回收的可回收对象,或者最坏情况下,每个发布者都有一个有限的订阅者数量可以被创建和放弃,那么就不会发生内存泄漏。
我的一个抱怨是.net没有提供事件清理的便利方式。这在vb.net中尤其令人恼火,因为它确保更改"WithEvents"变量将正确生成适当的订阅和取消订阅,但没有方便的方法让IDisposable处理程序取消对象持有的所有事件。

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