当WinForm发布其资源? (C#)

时间:2009-07-11 15:32:49

标签: c# .net winforms

这是一个C#WinForm问题。我有一个MDI子窗体,当窗体打开时,它将生成许多表以显示在DataGridView中。表可以很大,我使用DataBinding将表连接到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支持!我真的不明白:如果处理整个表单,为什么BindingList仍然存在于内存中。我已经检查了我的代码,并且没有BindingList引用泄露出来的地方。

知道为什么这个有线的事情发生在我身上?非常感谢.net内存管理方面的任何建议和提示。

修改

感谢您的回答。让我澄清一些观点。

  1. 我不是要处置BindingList。 BindingList在我的代码中用作数组,所以我希望看到BindingList保存的内存的回收。我不好不说清楚。

  2. 我明白不要自己打电话给GC。但在我的情况下,我希望在表单关闭后立即明确看到表单占用的内存回收,无论如何要做到这一点?示例代码?

  3. 在我的问题中,我可以多次重复打开相同的表单,每次关闭表单时,内存消耗都会增加,直到OutOfMemory抛出。这显然是不对的。我期望看到的是,在表单关闭后,内存会回落到原始级别,我可以重复打开表单并关闭它而不会增加内存(显着)。

  4. 编辑2:

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

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

7 个答案:

答案 0 :(得分:3)

除非你有充分的理由这样做,否则永远不要调用GC.Collect。

我不知道细节,但这就是我所知道的:

  • 删除所有引用后删除了某些内容。
  • GC不会立即尝试删除所有内容,也不想打扰程序运行。
  • 表单很有可能只是隐藏,而不是关闭。你如何关闭表格?
  • 作为Mitch Wheat says,事件处理程序可能很顽固,因为您经常忘记删除它们。
  • Dispose不会使表单从内存中删除,它在表单内部调用一个普通的方法,也许你可以在其中插入自己的清理逻辑。

答案 1 :(得分:2)

您确定要解除表单使用的所有事件处理程序吗?

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

答案 2 :(得分:0)

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

你如何使用objPassedIn?该对象是否可能保留引用并因此阻止GC收集它们?我还建议在FormClosing事件中显式清除对象,方法是将它们设置为null和/或对它们调用Dispose。正如@Mitch所提到的那样,对于生命周期比表单更长的对象上的事件没有取消订阅也可能是GC为什么没有收集你期望的对象的罪魁祸首。请记住,您可以在表单上调用Dispose,但只要您有对表单的引用,它就会存在,而不是垃圾回收。

TestForm frm = new TestForm();
frm.MyTextField = "Hello World";
frm.ShowDialog();
frm.Dispose();
string textField = frm.MyTextField;

这仍然有效。想象而不是一个简单的文本字段,你有List有数千个对象。但是,如果您尝试再次显示该表单,则会遇到问题,因为表单需要正确显示所有与UI相关的资源。

另外,假设您没有保留引用,则不需要显式强制执行垃圾收集。只要存在内存压力,就会发生GC。让它自然发生。如果您保留参考文件似乎是您的问题,那么无论您调用GC多少次,它都不会收集您期望的内容,因为它们仍然被引用。

答案 3 :(得分:0)

内存分析器不会告诉你什么是BindingList吗? 如果没有,this answer可能会有用。

答案 4 :(得分:0)

不要指望.NET会马上收集你的记忆。

阅读thisthis,以便更好地了解.NET如何处理GC。

并查看this以更好地了解如何解决问题。特别是阅读 什么如果物体生存部分。

答案 5 :(得分:0)

垃圾收集器在自己的时间运行。

WinForm和DataGridView是独立的,不同的对象。

BindingList是DataGridView的一部分,与Form无关。

当关闭持有DataGridView的WinForm时,你需要处理DataGridView。

应该在Form中的适当位置调用DataGridView.Dispose(),可能是FormClosing事件。

答案 6 :(得分:-1)

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

FormBigMemory f = new FormBigMemory(objPassedIn);
f.MdiParent = this;
f.Show();

是否有可能通过使用var(我的理解只是真正用于泛型),你以某种方式保持内存可用?我不太明白这会怎样,但可能值得谷歌搜索。

或许通过不定义MdiParent,你可以将表单保存在内存中吗?

如果有其他人可以阐明我的想法,那就太好了。试着解决这个问题。