.net - 在 Windows 窗体中添加和删除控件和内存使用

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

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

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、Dispose、manuel GC 调用。

用我的原始代码

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

使用处置

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

使用手动 GC 调用

我在 Dispose 之后添加了这段代码:

GC.Collect();
GC.WaitForPendingFinalizers();

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

我真的很喜欢使用 Dispose + GC 方法,但我所关注的关于手动 GC 调用的每一篇文章都强烈反对使用它。即使我没有任何自定义终结器/析构器,我也不确定是否使用它..

最佳答案

您可以使用 TaskMgr.exe、“进程”选项卡查看发生了什么。查看+选择列并勾选“用户对象”。此列跟踪窗口句柄。请注意,当您单击该按钮时,此列的值会不断增加。调用 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(),它将变成一个“僵尸”,一个不可见的窗口,您无法获取其控件包装器引用。

关于.net - 在 Windows 窗体中添加和删除控件和内存使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2032896/

相关文章:

c# - AES 加密。从 Python (pyCrypto) 到 .NET

c# - 如何获取控制台应用程序的屏幕大小?

c# - 如何访问继承表单上的控件?

c# - 带有 CaSTLe Windsor 的 Winforms MVP - 子窗体的 DI?

c# - 使用.net c# 将图像从 jpg 转换为特定的 TIFF 格式

c# - .Net C# String.Join 如果元素值为空,如何输出 "null"而不是空字符串?

c++ - 如何判断 const char* 是否指向有效字符串?

objective-c - 我是否需要从 dealloc 中的 super View 中删除 View ?

c# - 将 UserControl 添加到 C# 中的变量位置

memory-management - 如何划分分配区域,以便可以分别重新分配两个区域?