c# - GC 没有完成 UserControl?

标签 c# .net garbage-collection

我有一个 CF 应用程序,随着时间的推移会泄漏 UserControls。这花了一些时间,但我缩小了范围,甚至复制了完整框架 (3.5) 中的行为。由于两者都存在这种行为,我不想将其称为错误,但我确实不明白为什么会发生这种情况,希望有人能对此有所了解。

因此,我创建了一个简单的 WinForms 应用程序,其中包含一个表单和一个按钮。单击 Button 在创建新的 UserControl 和处理该控件之间交替。非常简单。

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();
        }
    }
}

这是用户控件。它只是跟踪事件(即未最终确定)实例的数量。它上面什么都没有,只有一个标签,所以我可以目视确认它在表格上。

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 的名义这是默认行为?

最佳答案

So why is this happening? Evidently the base is calling SuppressFinalize, but exactly why would this be happening, and why in the name of Odin is it the default behavior?

这是(正确地)实现 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);
}

关于c# - GC 没有完成 UserControl?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17331817/

相关文章:

c# - WPF 事件触发器

c# - 如何使用 C# 从包含大量其他文件或文件夹的文件夹中正确删除 .Net Core 中的文件

c# - 如何允许在 PropertyGrid 中修改嵌套类实例而无法访问已检查的类代码?

java - java.util.concurrent.ExecutorService#submit花费很长时间

java - 线程终止时jvm是否释放线程的堆栈

c# - C#/.NET (VS 2008) 到 Outlook (2007) 的问题

c# - 通过反射获取公共(public)静态字段的值

c# - 按嵌套列表排序的列表

c# - 让 AppSettingsReader 询问值的类型并返回一个非特定类型的对象有什么意义?

java - Class.forName 调用后如何释放内存?