这是一个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内存管理方面的任何建议和提示。
修改
感谢您的回答。让我澄清一些观点。
我不是要处置BindingList。 BindingList在我的代码中用作数组,所以我希望看到BindingList保存的内存的回收。我不好不说清楚。
我明白不要自己打电话给GC。但在我的情况下,我希望在表单关闭后立即明确看到表单占用的内存回收,无论如何要做到这一点?示例代码?
在我的问题中,我可以多次重复打开相同的表单,每次关闭表单时,内存消耗都会增加,直到OutOfMemory抛出。这显然是不对的。我期望看到的是,在表单关闭后,内存会回落到原始级别,我可以重复打开表单并关闭它而不会增加内存(显着)。
编辑2:
我进一步调查了代码。表单关闭后,不会释放最大的数组(BindingList)。它由BindingSource引用,BindingSource由EventHandler,CurrencyManager和用户控件引用。 BindingSource和用户控件都由(闭合)表单引用,而(闭合)表单由某些事件处理程序引用。
以上是关闭表单后的内存快照。我使用对话框模型打开表单,在它关闭后,调用它的Dispose()方法,之后我还调用GC.Collect()来强制回收内存。为什么在此之后表单实例仍存在于我的内存快照中?
答案 0 :(得分:3)
除非你有充分的理由这样做,否则永远不要调用GC.Collect。
我不知道细节,但这就是我所知道的:
答案 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)
答案 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,你可以将表单保存在内存中吗?
如果有其他人可以阐明我的想法,那就太好了。试着解决这个问题。