WinForm何时释放其资源?(C#)

WinForm何时释放其资源?(C#),c#,.net,winforms,C#,.net,Winforms,这是一个C#WinForm问题。我有一个MDI子窗体,当窗体打开时,它将生成许多表以显示在DataGridView中。表可以很大,我使用数据绑定将表连接到DataGridView。当我关闭表单时,我注意到表单占用的内存被及时回收 我使用常规方式将MDI子窗体显示为: var f = new FormBigMemory(objPassedIn); f.Show(); 如图所示,我无法显式调用表单的Dispose()方法。我假设当f超出其生命周期时,.net将自动回收它所占用的内存 由于表单占用了

这是一个C#WinForm问题。我有一个MDI子窗体,当窗体打开时,它将生成许多表以显示在DataGridView中。表可以很大,我使用数据绑定将表连接到DataGridView。当我关闭表单时,我注意到表单占用的内存被及时回收

我使用常规方式将MDI子窗体显示为:

var f = new FormBigMemory(objPassedIn);
f.Show();
如图所示,我无法显式调用表单的Dispose()方法。我假设当f超出其生命周期时,.net将自动回收它所占用的内存

由于表单占用了大量内存,我想显式调用GC.Collect();(我知道这样做可能不是个好主意)。为此,我使用以下代码更改代码以在对话框模型中显示表单:

using(var f = new FormBigMemoryDialog(objPassedIn);
{
    f.ShowDialog();
}

GC.Collect();
在调用GC.Collect()之后,表单占用的内存没有被回收,这让我非常失望。在关闭表单并调用GC.Collect()之后,我使用内存探查器工具获取内存的快照。最大的内存被绑定列表占用!我真的不明白:如果整个表单都被释放了,为什么绑定列表仍然存在于内存中。我已经检查了我的代码,没有发现BindingList引用从表单中泄露出来

你知道我为什么会遇到这种情况吗?非常感谢关于.net内存管理的任何建议和提示

编辑:

谢谢你的回答。让我澄清几点

  • 我不是要处理绑定列表。BindingList在我的代码中用作数组,因此我希望看到BindingList所持有的内存的回收。我的缺点是没有说清楚

  • 我知道我自己不会调用GC操作。但是在我的例子中,我希望在表单关闭后立即显式地看到表单占用的内存的回收,无论如何,这样做吗?示例代码

  • 在我的问题中,我可以多次重复打开同一个表单,每次关闭表单时,内存消耗都会增加,直到OutOfMemory抛出。这显然是不对的。我希望看到的是,关闭表单后,内存会下降到原始级别,我可以重复打开表单并关闭它,而不会增加内存(显著增加)

  • 编辑2:

    我进一步调查了代码。最大的数组(BindingList)在表单关闭后不会释放。它由BindingSource引用,BindingSource由EventHandler、CurrencyManager和用户控件引用。BindingSource和用户控件都由(关闭的)窗体引用,(关闭的)窗体由一些事件处理程序引用


    上面是我关闭表单后的内存快照。我使用对话框模型打开表单,关闭表单后,调用其Dispose()方法,然后调用GC.Collect()强制回收内存。为什么在这之后表单实例仍然存在于我的内存快照中???

    除非有很好的理由,否则不要调用GC.Collect

    我不知道细节,但我知道:

    • 删除所有引用后,会删除某些内容
    • GC不会试图一次删除所有内容,它不想干扰程序的运行
    • 表单很有可能只是隐藏起来,而不是关闭。如何关闭表单
    • 例如,事件处理程序可能很顽固,因为您经常忘记删除它们
    • Dispose不会将表单从内存中删除,它会在表单中调用一个普通方法,也许您可以在其中插入自己的清理逻辑
    是否确实正在取消窗体使用的所有事件处理程序的调用


    更新以响应注释中的问题:每当对象向事件(+=EventHandler)添加事件处理程序时,当您完成对象实例时,应该有相应的(-=EventHandler)。否则,它将充当堆中的根对象,垃圾回收将不会回收。

    我唯一不同的做法是在创建新表单时使用var。我会像使用普通定义一样使用类名

    FormBigMemory f = new FormBigMemory(objPassedIn);
    f.MdiParent = this;
    f.Show();
    
    有没有可能通过使用var(据我所知,它只适用于泛型)让内存保持可用?我不太明白这将如何发生,但它可能值得谷歌搜索

    或者,通过不定义MDI父对象,您使窗体在内存中保持活动状态


    如果有人能告诉我这些想法,那就太好了。尝试解决这个问题。

    为什么希望Form.Dispose()清理BindingList?如果您谈论的是System.ComponentModel中的BindingList,那么它甚至不是一次性的。我不太确定Form处理资源的深度。它可以迭代其控件集合,并对实现IDisposable的控件调用Dispose。但是,如果在诸如DataSet或List之类的表单上定义字段并用大量数据填充该字段,则在该表单上调用Dispose不会影响该字段占用的内存。如果您希望该字段是GCD,那么您只需要确保它不是从任何地方引用的

    如何使用objPassedIn?这个对象是否可能保留引用,从而阻止GC收集它们?我还建议在FormClosing事件中通过将对象设置为null和/或调用Dispose来显式清理对象。正如@Mitch所提到的,没有取消订阅比表单生命周期更长的对象上的事件也可能是GC没有收集您期望的对象的原因。请记住,您可以对表单调用Dispose,但只要您有对该表单的引用,它就会继续存在,而不会被垃圾收集

    TestForm frm = new TestForm();
    frm.MyTextField = "Hello World";
    frm.ShowDialog();
    frm.Dispose();
    string textField = frm.MyTextField;
    
    这仍然有效。想象一下,你有了Li,而不是一个简单的文本字段