为什么没有得到垃圾回收已关闭的最后一个MDI子窗体?(Why does the last MDI c

2019-08-17 08:24发布

我们已经与我们的应用程序的内存泄漏问题。 我已经成功地复制与下面的简单示例的问题之一:

复制设置

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子窗体,这样其他的人可以被垃圾收集 - 但为什么我应该这样做吗? 到底是怎么回事?

此外,它是一个有点难看,你会得到的形式开启和关闭的闪烁,它似乎很哈克过。

Answer 1:

从技术上讲,因为Form是“FormerlyActiveMdiChild”。 这看起来像一个错误。 幸运的是,没有一个十分严重的问题。

解决未收对象的能力是个好技能有。 从微软WinDbg调试器附带的Windows调试工具( http://www.microsoft.com/whdc/devtools/debugging/default.mspx )是为这个伟大的目的。 在下面的演练,请注意,我已删除了大量的输出从WinDbg中是不恰当的。

  1. 而不是创造型的MDI子实例的Form ,继承它作为TestChildForm使其易于识别。
  2. 启动可执行文件,并连接WinDbg。 加载与.NET扩展!loadby sos mscorwks
  3. 在WinDbg中,跑!dumpheap -type TestChildForm

      Address MT Size 01e2e960 001c650c 320 
  4. 接下来,运行!gcroot 01e2e960

     ESP:3de7fc:Root:01e29a78(System.EventHandler)-> 01e26504(WindowsFormsApplication1.Form1)-> 01e269b8(System.Windows.Forms.PropertyStore)-> 01e2ef04(System.Windows.Forms.PropertyStore+ObjectEntry[]) 
  5. 接下来,运行!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 
  6. 最后,我跑了!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



Answer 2:

为什么出现这种情况的原因很简单,仍然有这种形式的参考。 好消息是,我们可以删除此引用。

添加事件处理程序的形式结束活动。

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?