在WindowsForm中添加和删除控件和内存使用情况

dst*_*str 2 .net user-controls dispose memory-management winforms

我有一个带有面板控件的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());
Run Code Online (Sandbox Code Playgroud)

我只是单击了使用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();
Run Code Online (Sandbox Code Playgroud)

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

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

Han*_*ant 5

您可以使用TaskMgr.exe,Processes选项卡查看正在发生的情况.查看+选择列并勾选"USER对象".此列跟踪窗口句柄.请注意,单击按钮时此列的值会不断增加.调用GC.Collect()不会使其失效.一旦达到10,000,您的程序将崩溃并刻录.

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

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

  while (panel.Controls.Count > 0) panel.Controls[0].Dispose();
Run Code Online (Sandbox Code Playgroud)

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

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