在Windows窗体中添加和删除控件和内存使用情况

时间:2010-01-09 09:14:11

标签: .net winforms memory-management user-controls dispose

我有一个带有面板控件的WindowsForm,我用它来显示我的UserControls。我以这种方式添加控件:

private void AddControl(Control control)
{
    panel.Controls.Clear();
    control.Size = new Size(panel.Width - 1, panel.Height - 1);
    control.Anchor = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles. Top;
    panel.Controls.Add(control);
}

..

AddControl(new ucSomeControl());

我只是单击了使用AddControl()的每个按钮,并且每次都看到内存使用量增加。我让应用程序运行,什么都不做,一个半小时,内存使用量从140mb减少到138mb,就像2mbs一样。你认为这是正常的还是我在控制添加方法上做错了什么我应该/可以改进以减少内存使用?

跟进

我已经创建了4个版本的应用程序:Debug,Release,with Dispose,with manuel GC call。

使用原始代码

我的应用程序的调试版本和发布版本在内存使用方面没有什么区别,比如5mb。这些版本的问题在于,我点击按钮的次数越多,即使我单击相同的按钮并再次创建相同的UserControl,内存使用量也会同样增加。

使用Dispose

我添加了Chris Arnold的Dispose代码。内存使用率显着降低,虽然创建越来越多的控件仍然会增加内存使用量,但现在每个控件使用的内存要少得多。这是一个有价值的附录。

使用manuel GC致电

我在Dispose:

之后添加了这段代码
GC.Collect();
GC.WaitForPendingFinalizers();

宾果!内存使用量甚至比Dispose代码少。最好的部分是,即使我一遍又一遍地创建新的控件,内存使用量的增加也非常小,几乎是微不足道的。

我真的很喜欢使用Dispose + GC方法,但是每篇关于manuel GC调用的文章都非常令人沮丧。即使我没有任何自定义终结器/析构器,我也不确定是否使用它。

3 个答案:

答案 0 :(得分:5)

您可以使用TaskMgr.exe,进程选项卡查看正在进行的操作。查看+选择列并勾选“USER对象”。此列跟踪窗口句柄。请注意,单击按钮时此列的值会不断增加。调用GC.Collect()不会使其失效。一旦达到10,000,你的程序就会崩溃并烧掉。

Control类是.NET框架中唯一的类,我知道需要调用Dispose()。终结器不足以确保释放窗口句柄。调用Dispose()通常是完全自动的,控件的父级在处理时会执行它。最终的父对象是Form对象,它在关闭时会自动处理它(及其子控件)。

但是当您自己从Controls集合中删除控件时,这不会发生。你不能使用Clear()方法,你必须这样做:

  while (panel.Controls.Count > 0) panel.Controls[0].Dispose();

它以这种方式工作的原因是窗口的生命周期由Windows管理,而不是程序。只要窗口处于活动状态,控制包装器就不应该被垃圾收集。 Windows窗体跟踪内部表中的窗口句柄。只要Handle有效,该表就确保不能对Control类包装器进行垃圾回收。换句话说,始终至少有一个对Control对象的引用。

在窗口获取WM_NCDESTROY消息之前,不会从该内部表中删除该引用,这是窗口过程在销毁窗口句柄之前接收的最后一条消息。从Controls集合中删除控件不足以破坏窗口。如果你没有显式调用Dispose(),它将变成一个“僵尸”,一个不可见的窗口,你无法获得它的Control包装引用。

答案 1 :(得分:2)

您可以在清除之前尝试处理Controls集合。像这样......

private void AddControl(Control control)
{
    foreach (IDisposable control in panel.Controls)
      control.Dispose();

    panel.Controls.Clear();
    control.Size = new Size(panel.Width - 1, panel.Height - 1);
    control.Anchor = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles. Top;
    panel.Controls.Add(control);
}

答案 2 :(得分:1)

由于垃圾收集器,这是非常正常的行为。在创建对象时立即分配内存,但不立即释放。后来,当垃圾收集器运行时,它注意到有些对象不再引用它们,所以它释放了内存。

This SO post在其答案中有一些链接,提供有关GC的更多信息。