C# GC没有完成UserControl?

C# GC没有完成UserControl?,c#,.net,garbage-collection,C#,.net,Garbage Collection,我有一个CF应用程序,随着时间的推移会泄漏用户控件。这需要一些时间,但我缩小了范围,甚至在完整的框架(3.5)中复制了该行为。由于这两种行为都存在,我不想称之为bug,但我确实不明白为什么会发生这种情况,希望有人能对此有所了解 因此,我创建了一个简单的WinForms应用程序,其中包含一个表单和一个按钮。单击该按钮可在创建新的UserControl和处理该控件之间切换。很简单 public partial class Form1 : Form { public Form1() {

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

因此,我创建了一个简单的WinForms应用程序,其中包含一个表单和一个按钮。单击该按钮可在创建新的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());
    }
}
奇怪的是,实例数无限增长。最终,在这个设备上,我的内存用完了。我想我也会在电脑上,我只是不想在明年点击这个按钮

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

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

    base.Dispose(disposing);

    if (disposing)
    {
        GC.ReRegisterForFinalize(this);
    }
}
然后,它的行为完全符合预期,在收集过程中完成实例(手动或自动)

那么为什么会发生这种情况呢?很明显,base正在调用SuppressFinalize,但这到底是为什么会发生的,以及为什么它以Odin的名义是默认行为

那么为什么会发生这种情况呢?很明显,base正在调用SuppressFinalize,但这到底是为什么会发生的,以及为什么它以Odin的名义是默认行为

这是类(正确地)实现
IDisposable
的默认行为。调用
IDisposable.Dispose
时,默认的建议行为是禁止终结,因为终结的主要原因是清理从未被处置的资源。这是因为完成是一项昂贵的操作—您不想不必要地完成对象,如果调用了
Dispose
,您的想法是您已经清理了非托管资源。任何托管内存都将得到处理

您应该覆盖
Dispose
,并在
Dispose
覆盖中执行减量

此行为在中进行了解释。示例
Dispose
方法调用实现是(来自参考文档):


+ 1,实际上有一个真正的理由考虑使用GC,(抱歉不能帮助解决实际问题!)考虑到里德的答案,你的真正代码中的终结器是做什么的?不能调用终结器导致泄漏吗?真正的代码根本没有终结器。他们已经实现了Dispose,位图和类似的东西正在被处理。你的复制代码不能证明任何事情。您还没有消除Dispose()方法中出现简单错误的可能性,忘记处理某些内容。当涉及位图且GC运行不够频繁时,这可能会让你感到不适。当应用程序空闲时,GC会以非常可重复的30秒周期运行,在I/O负载下运行得更快。我可以接受,我可能在这里偏离了基准-这不是第一次-但让我感到困惑的是,加入ReRegisterForFinalize修复了OOM(或者至少在没有它的系统中会显著延迟OOM-我需要几天的测试才能确定)。唯一的问题是,在实际的应用程序环境中,当您在应用程序中导航时,内存会向上爬行,而GC永远不会恢复该内存。幸运的是,我的所有视图都来自一个基本用户控件。当我在那个基地重新注册后,漏洞就消失了。我的所有视图都没有实现终结器。根据关联,这似乎是问题的原因。让我拒绝这条道路的是使用.NET内存分析器,它说有“已处理但未最终确定”的视图。
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);
}