C# 事件内存泄漏 [英] C# Events Memory Leak

查看:34
本文介绍了C# 事件内存泄漏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这些未订阅的事件何时发生内存泄漏?我应该编写析构函数还是实现 IDisposable 来取消订阅事件?

解决方案

假设 A 引用 B.此外,假设您认为 B 已经完成并希望它被垃圾收集.

现在,如果 A可达[1],B 将不会被垃圾收集,尽管事实上你是完成它".从本质上讲,这是一个内存泄漏[2]

如果B订阅了A中的一个事件,那么我们有同样的情况:A引用了B 通过事件处理程序委托.

那么,这是什么时候出现的问题?仅当引用对象可达时,如上所述.在这种情况下,当 Foo 实例不再使用时,可能会发生泄漏:

class Foo{酒吧_酒吧;公共 Foo(酒吧酒吧){_bar = 酒吧;_bar.Changed += BarChanged;}void BarChanged(object sender, EventArgs e) { }}

可能泄漏的原因是构造函数中传递的Bar实例可以具有比更长的生命周期>Foo 实例使用它.订阅的事件处理程序可以然后保持 Foo 活着.

在这种情况下,您需要提供一种取消订阅事件的方法,以免发生内存泄漏.一种方法是让 Foo 实现 IDisposable.这样做的好处是它清楚地向类消费者发出信号,表明他需要在完成后调用 Dispose().另一种方法是使用单独的 Subscribe()Unsubscribe() 方法,但这并不能传达类型的期望 - 它们太可选了,无法调用并引入时间耦合.

我的建议是:

class 密封 Foo : IDisposable{只读栏_bar;布尔_处置;...公共无效处置(){如果(!_处置){_disposed = 真;_bar.Changed -= BarChanged;}}...}

或者:

class 密封 Foo : IDisposable{酒吧_酒吧;...公共无效处置(){如果 (_bar != null){_bar.Changed -= BarChanged;_bar = 空;}}...}

另一方面,当引用对象不可可达时,不可能泄漏:

 类密封的 Foo{酒吧_酒吧;公共 Foo(){_bar = new Bar();_bar.Changed += BarChanged;}void BarChanged(object sender, EventArgs e) { }}

在这种情况下,任何 Foo 实例将始终比其组合的 Bar 实例寿命更长.当 Foo 无法访问时,它的 Bar 也将无法访问.订阅的事件处理程序无法在此处保持 Foo 活动.这样做的缺点是,如果 Bar 是一个需要在单元测试场景中模拟的依赖项,它不能(以任何干净的方式)由消费者显式实例化,但需要注入.

[1] http://msdn.microsoft.com/en-us/magazine/bb985010.aspx

[2] http://en.wikipedia.org/wiki/Memory_leak>

When does these unsubscribed events memory leak occurs? Should I write destructor or implement IDisposable to unsubscribe an event?

解决方案

Let's say that A references B. Furthermore, say you think you're done with B and expect it to be garbage collected.

Now, if A is reachable[1], B won't be garbage collected, despite the fact that "you're done with it". This is, in all essence, a memory leak[2]

If B subscribes to an event in A, then we have the same situation: A has a reference to B via the event handler delegate.

So, when is this a problem? Only when the referencing object is reachable, as mentioned above. In this case, there can be a leak when a Foo instance isn't used any longer:

class Foo
{
    Bar _bar;

    public Foo(Bar bar)
    {
        _bar = bar;
        _bar.Changed += BarChanged;
    }

    void BarChanged(object sender, EventArgs e) { }
}

The reason why there can be a leak is that the Bar instance passed in the constructor can have a longer lifetime than the Foo instance using it. The subscribed event handler can then keep the Foo alive.

In this case you need to provide a way to unsubscribe from the event to not get a memory leak. One way of doing that is by letting Foo implement IDisposable. The upside of that is that it clearly signals to the class consumer that he need to call Dispose() when done. Another way is to have separate Subscribe() and Unsubscribe() methods, but that doesn't convey the type's expectations - they are too optional to call and introduce a temporal coupling.

My recommendation is:

class sealed Foo : IDisposable
{
    readonly Bar _bar;
    bool _disposed;

    ...

    public void Dispose()
    {
        if (!_disposed)
        {
            _disposed = true;
            _bar.Changed -= BarChanged;
        }
    }

    ...
}

Or alternatively:

class sealed Foo : IDisposable
{
    Bar _bar;

    ...

    public void Dispose()
    {
        if (_bar != null)
        {
            _bar.Changed -= BarChanged;
            _bar = null;
        }
    }

    ...
}

On the other hand, when the referencing object isn't reachable, there can't be a leak:

class sealed Foo
{
    Bar _bar;

    public Foo()
    {
        _bar = new Bar();
        _bar.Changed += BarChanged;
    }

    void BarChanged(object sender, EventArgs e) { }
}

In this case any Foo instance will always outlive its composed Bar instance. When a Foo is unreachable, so will its Bar be. The subscribed event handler cannot keep the Foo alive here. The downside of this is that if Bar is a dependency in need of being mocked in unit testing scenarios, it can't (in any clean way) be explicitly instantiated by the consumer, but needs to be injected.

[1] http://msdn.microsoft.com/en-us/magazine/bb985010.aspx

[2] http://en.wikipedia.org/wiki/Memory_leak

这篇关于C# 事件内存泄漏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆