C#活动内存泄漏[关闭](C# Events Memory Leak [closed])

2019-06-27 10:09发布

什么时候发生这些事件退订内存泄漏? 我应该写析构函数或实现IDisposable退订的事件吗?

Answer 1:

比方说,A引用B中 。 此外,说你觉得你为B完成,并期望它被垃圾收集。

现在, 如果 可达 [1],B不会被垃圾收集,尽管事实上,“你用它做”。 这是,在所有的本质, 内存泄漏 [2]

B订阅在一个事件的话,我们有同样的情况: 一个具有参考通过事件处理程序委托给B。

所以,当这是个问题吗? 只有当引用对象是可到达的,如上所述。 在这种情况下, 可以存在一个泄漏时一个Foo实例不使用任何长:

class Foo
{
    Bar _bar;

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

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

为什么可以有泄漏的原因是,在构造函数中通过了律师实例可以比使用它的例如更长的寿命。 然后,订阅事件处理程序可以保持美孚活着。

在这种情况下,您需要提供一种方法从事件退订不了内存泄漏。 这样做的一种方式是通过让实现IDisposable。 那好处是,它清楚地表明对类消费者,他需要在完成时调用Dispose()。 另一种方法是有独立的订阅()退订()方法,但这并不传达类型的期望-他们太随意调用,并引入时间耦合。

我的建议是:

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

    ...

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

    ...
}

或者:

class sealed Foo : IDisposable
{
    Bar _bar;

    ...

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

    ...
}

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

class sealed Foo
{
    Bar _bar;

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

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

在这种情况下,任何的Foo实例将始终活得比它的组成实例。 当不可达,所以将其酒吧 。 订阅的事件处理程序不能保持美孚这里存活。 这种方法的缺点是,如果酒吧是需要在单元测试场景被嘲笑的依赖,就不能(在任何清洁方式)由消费者明确实例化,但需要注入。

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

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



Answer 2:

事件处理程序包含一个强引用声明处理程序(在代理的对象Target属性)。

直到该事件处理程序被移除(或直到拥有该事件的对象不再引用的任何地方),将含有处理程序的对象将不会被收集。

您可以通过删除处理程序解决这个问题,当你不再需要它(也许在Dispose() )。
终结不能帮助,因为它可以被收集后的终结将只运行。



文章来源: C# Events Memory Leak [closed]