C# 内存泄漏和处置

C# 内存泄漏和处置,c#,memory-leaks,C#,Memory Leaks,我可能不明白这个意思,或者我做错了什么。 我对.NET中的内存管理有一些问题 想象一下情况: Form1是大人物表单,作为MDI父级,小FormChild绑定为子级: public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void simpleButton1_Click(object

我可能不明白这个意思,或者我做错了什么。 我对.NET中的内存管理有一些问题

想象一下情况:

Form1
是大人物表单,作为MDI父级,小
FormChild
绑定为子级:

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

        private void simpleButton1_Click(object sender, EventArgs e)
        {
            FormChild formChild = new FormChild();

            formChild.MdiParent = this;
            formChild.Show();

        }
    }
现在,孩子正在分配一点内存作为模拟:

public partial class FormChild : Form
{
    private readonly List<byte[]> _list = new List<byte[]>();

    public FormChild()
    {
        InitializeComponent();


    }

    private void FormChild_Load(object sender, EventArgs e)
    {
        int i = 0;
        while (i < 100)
        {
            _list.Add(new byte[1024 * 1024 * 10]);
            i += 1;
        }

    }

}
公共部分类FormChild:Form
{
私有只读列表_List=new List();
公共表格儿童()
{
初始化组件();
}
私有void FormChild_加载(对象发送方,事件参数e)
{
int i=0;
而(i<100)
{
_添加(新字节[1024*1024*10]);
i+=1;
}
}
}
现在,我用内存分析器检查内存堆中发生了什么。 我明白了,如果我点击这个按钮,内存就被分配了。然后我关闭
FormChild
,它调用
Dispose()
。但是内存仍然被分配。如果我再次单击,将发生
系统。OutOfMemoryException

为什么GC在等待释放托管内存?
或者这是我的设计错误?

GC仅在内存压力下释放内存,Dispose的主要目的是清理与内存无关的资源

protected override void Dispose(bool disposing)
{
    if (disposing)
    {
        if (_list != null)
        {
            // Clear it, dispose it, do whatever you want with it.
        }
    }

    base.Dispose(disposing);
}

换句话说,清空托管对象不一定会使它们更快地被收集,但会使诊断内存问题更容易诊断。

FormChild
中覆盖
Dispose
,以便您可以清理资源

protected override void Dispose(bool disposing)
{
    if (disposing)
    {
        if (_list != null)
        {
            // Clear it, dispose it, do whatever you want with it.
        }
    }

    base.Dispose(disposing);
}

正如Yaur所说,当调用
Dispose
时,GC不会立即清理内存,但最好尽可能地清理内存。

可以使用GC.Collect()立即执行垃圾收集


但是,我通常让它自己处理并实现Dispose方法。

这看起来像是某种计时问题,在创建第二个实例时,formChild的第一个实例仍然是可访问的(即不是垃圾)。您不能容纳该列表两次

请注意,我关闭FormChild,它调用Dispose()是关于资源和窗口句柄的语句,而不是关于释放内存的语句

不清楚您是否编写了自己的Dispose(),但在这种(相当特殊的)情况下,您应该这样做

  • FormChild.Designer.cs
    文件中剪切
    void Dispose(bool disposing)
    方法,并将其移动到
    FormChild.cs

  • 使用它释放巨大的内存块:

    protected override void Dispose(bool disposing)
    {
        _list = null;  // add this
    
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }
    

请注意,这不是一种“常见”的内存管理形式,但它是必需的,因为您的_列表也不常见

是的,但是第二次点击我的按钮以装入第二个MDI
FormChild
是否压力不足?这取决于机器中的ram数量。此外,您正在处理大对象堆上收集次数较少的对象(有关更多详细信息,请参阅)。在关闭/释放子窗体时,请尝试将MdiParent重置为null。这是一个可能将子窗体保持在范围内的引用。您的测试有点无效,因为您创建了将在大对象堆(LoH)上分配的巨大数组。我敢打赌,由于托管内存碎片,您将遇到OutofMemoryException。在
Dispose()
@Jf Beaulac:不,绝对不会。我在我的分析器(ANTS memory profiler)中看到,大型对象堆碎片没有问题。这是正确的代码吗?因为像formChild的作用域和while循环的位置这样的小事非常重要。这是处理托管内存,而不是非托管资源。它与
Dispose
毫无关系。这是一个部分答案,但我补充了一个理论,说明它可能会有所帮助。Dispose()与GCNo关系不大。我不会自己写
dispose()在FormCosed事件中。您的代码有效。但我还是不明白为什么我的_清单应该与众不同?你能告诉我吗?你的单子很特别,因为它太大了。一份较小的清单最终可能会被回收,但现在时机至关重要。这是关于GC和可达性,而不是关于关闭或(正常的)处理。基本上,只要你不再需要它,就设置
\u list=null
。限制它的生存期,并注意存储引用的位置。如果它有Dispose(),那么首先调用它。