GC没有最终确定UserControl?

时间:2013-06-26 22:55:16

标签: c# .net garbage-collection

我有一个CF应用程序,随着时间的推移泄漏UserControls。花了一些时间,但我把它缩小了,甚至在完整的框架中复制了这个行为(3.5)。既然这两种行为都存在,我不想把它称为bug,但我肯定不明白为什么会发生这种情况,并希望有人能够对它有所了解。

所以我创建了一个带有Form和Button的简单WinForms应用程序。单击按钮可在创建新UserControl和Disposing该控件之间切换。非常简单。

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    UserControl1 m_ctl;

    private void button1_Click(object sender, EventArgs e)
    {
        if (m_ctl == null)
        {
            m_ctl = new UserControl1();
            m_ctl.Visible = true;
            this.Controls.Add(m_ctl);
        }
        else
        {
            this.Controls.Remove(m_ctl);
            m_ctl.Dispose();
            m_ctl = null;
            GC.Collect();
        }
    }
}

这是UserControl。它只是跟踪实时(即未完成)实例的数量。它没有任何内容,只有一个标签,所以我可以直观地确认它在表格上。

public partial class UserControl1 : UserControl
{
    private static int m_instanceCount = 0;

    public UserControl1()
    {
        var c = Interlocked.Increment(ref m_instanceCount);
        Debug.WriteLine("Instances: " + c.ToString());

        InitializeComponent();
    }

    ~UserControl1()
    {
        var c = Interlocked.Decrement(ref m_instanceCount);
        Debug.WriteLine("Instances: " + c.ToString());
    }
}

这里奇怪的是实例计数无限增长。最终,在设备上,我的内存不足。我怀疑我也会在PC上,我只是不想点击明年的按钮。

现在,如果我改变UserControl的默认设计器生成的Dispose方法,只需添加ReRegisterForFinalize调用:

protected override void Dispose(bool disposing)
{
    if (disposing && (components != null))
    {
        components.Dispose();
    }

    base.Dispose(disposing);

    if (disposing)
    {
        GC.ReRegisterForFinalize(this);
    }
}

然后它的行为完全符合预期,在收集期间完成实例(手动或自动时)。

那为什么会这样呢?显然,基地正在调用SuppressFinalize,但究竟为什么会发生这种情况,为什么以Odin的名义它是默认行为?

1 个答案:

答案 0 :(得分:4)

  

那为什么会这样呢?显然,基地正在调用SuppressFinalize,但究竟为什么会发生这种情况,为什么以Odin的名义它是默认行为?

这是实现IDisposable的类(正确)的默认行为。当您调用IDisposable.Dispose时,默认的建议行为是禁止最终确定,因为最终确定的主要原因是清理从未放置的资源 。这是因为最终化是一项昂贵的操作 - 您不希望不必要地完成对象,并且如果调用Dispose,则认为您已经清理了非托管资源。无论如何都会处理任何托管内存。

您应该覆盖Dispose,并在Dispose覆盖范围内进行减量。

documentation for IDisposable中解释了此行为。样本Dispose方法调用实现(来自参考文档):

public void Dispose()
{
    Dispose(true);
    // This object will be cleaned up by the Dispose method. 
    // Therefore, you should call GC.SupressFinalize to 
    // take this object off the finalization queue 
    // and prevent finalization code for this object 
    // from executing a second time
    GC.SuppressFinalize(this);
}