我们已经与我们的应用程序的内存泄漏问题。 我已经成功地复制与下面的简单示例的问题之一:
复制设置
1)创建将被用于跟踪对象创建/销毁以下辅助类。
public class TestObject
{
public static int Count { get; set; }
public TestObject()
{
Count++;
}
~TestObject()
{
Count--;
}
}
2)用三个按钮创建一个MDI形式,第一个按钮将如下创建一个新的MDI子:
private void ctlOpenMDI_Click(object sender, EventArgs e)
{
Form newForm = new Form();
newForm.MdiParent = this;
newForm.Tag = new TestObject();
newForm.Show();
}
第二个按钮将被用来做相同,但与非MDI子窗体:
private void ctlOpenNonMDIForm_Click(object sender, EventArgs e)
{
Form newForm = new Form();
newForm.Tag = new TestObject();
newForm.Show();
}
第三个按钮将被用来收集垃圾,然后显示多少TestObject的情况下,是活的:
private void ctlCount_Click(object sender, EventArgs e)
{
GC.Collect();
GC.WaitForPendingFinalizers();
MessageBox.Show("Count: " + TestObject.Count);
}
复制步骤
1)点击打开MDI表单按钮,然后关闭MDI窗体,然后点击计算按钮。 它将返回计数:1,MDI子窗体和对象引用它不是垃圾收集 - 这还必须有一个参考吧。
也:
点击打开的MDI形式三次,关闭所有3种形式,然后点击计算按钮。 它将返回计数:1。它好像上次关闭的MDI子窗体没有垃圾收集。
反例:
1)点击打开非MDI形式,将其关闭。 然后点击计算按钮。 它将返回计数:收集0,形式和对象已经垃圾。
解决方法
我可以这样做解决此问题:
Form form = new Form();
form.MdiParent = this;
form.Show();
form.Close();
垃圾收集之前。 这使得该虚拟形式上次关闭的MDI子窗体,这样其他的人可以被垃圾收集 - 但为什么我应该这样做吗? 到底是怎么回事?
此外,它是一个有点难看,你会得到的形式开启和关闭的闪烁,它似乎很哈克过。
从技术上讲,因为Form
是“FormerlyActiveMdiChild”。 这看起来像一个错误。 幸运的是,没有一个十分严重的问题。
解决未收对象的能力是个好技能有。 从微软WinDbg调试器附带的Windows调试工具( http://www.microsoft.com/whdc/devtools/debugging/default.mspx )是为这个伟大的目的。 在下面的演练,请注意,我已删除了大量的输出从WinDbg中是不恰当的。
- 而不是创造型的MDI子实例的
Form
,继承它作为TestChildForm
使其易于识别。 - 启动可执行文件,并连接WinDbg。 加载与.NET扩展
!loadby sos mscorwks
。 在WinDbg中,跑!dumpheap -type TestChildForm
。
Address MT Size 01e2e960 001c650c 320
接下来,运行!gcroot 01e2e960
。
ESP:3de7fc:Root:01e29a78(System.EventHandler)-> 01e26504(WindowsFormsApplication1.Form1)-> 01e269b8(System.Windows.Forms.PropertyStore)-> 01e2ef04(System.Windows.Forms.PropertyStore+ObjectEntry[])
接下来,运行!dumparray -details 01e2ef04
和搜索输出01e2e960
。
MT Field Offset Type VT Attr Value Name 6797ea24 40032a3 10 System.Int16 1 instance 56 Key 6797ea24 40032a4 12 System.Int16 1 instance 1 Mask 6798061c 40032a5 0 System.Object 0 instance 01e2e960 Value1
最后,我跑了!name2ee System.Windows.Forms.dll System.Windows.Forms.Form
,接着!dumpclass 6604cb84
(如由确定的!name2ee
)看着56。
MT Field Offset Type VT Attr Value Name 67982c4c 4001e80 fd8 System.Int32 1 static 56 PropFormerlyActiveMdiChild
如果您愿意使用Visual Studio调试器,而不是WinDbg中,您必须首先启用属性,调试,启用非托管代码调试。 替代.load sos
为.loadby sos mscorwks
。
为什么出现这种情况的原因很简单,仍然有这种形式的参考。 好消息是,我们可以删除此引用。
添加事件处理程序的形式结束活动。
private void ctlOpenMDI_Click(object sender, EventArgs e)
{
Form newForm = new Form();
newForm.FormClosing += new FormClosingEventHandler(form_Closing);
newForm.MdiParent = this;
newForm.Tag = new TestObject();
newForm.Show();
}
和方法来处理事件。
private void form_Closing(object sender, EventArgs e)
{
Form form = sender as Form;
form.MdiParent = null;
}
在这里,我们重新设置MdiParent属性,通过这样的形式是从父的将MDIChild列表中删除。 现在,当窗体关闭该引用将重置为好。
文章来源: Why does the last MDI child form that was closed not get garbage collected?