Winforms MDI WinForm应用程序和复制子窗体内存泄漏

Winforms MDI WinForm应用程序和复制子窗体内存泄漏,winforms,c#-3.0,mdi,Winforms,C# 3.0,Mdi,这是WinForm MDI应用程序问题(.net framework 3.0)。这将用C#来描述。抱歉,时间有点长,因为我尽量把事情说清楚 我有一个MDI应用程序。在某个时候,我发现一个MDI子表单从未发布过。有一个菜单可以创建MDI子窗体并显示它。当MDI子窗体关闭时,应该销毁它,并将其占用的内存返回给.net。但令我惊讶的是,事实并非如此。所有MDI子表单实例都保存在内存中。这显然是一个“内存泄漏”。嗯,这并不是.net中真正的漏洞。只是我认为封闭形式应该是死的,但不知何故,至少有一个来自外

这是WinForm MDI应用程序问题(.net framework 3.0)。这将用C#来描述。抱歉,时间有点长,因为我尽量把事情说清楚

我有一个MDI应用程序。在某个时候,我发现一个MDI子表单从未发布过。有一个菜单可以创建MDI子窗体并显示它。当MDI子窗体关闭时,应该销毁它,并将其占用的内存返回给.net。但令我惊讶的是,事实并非如此。所有MDI子表单实例都保存在内存中。这显然是一个“内存泄漏”。嗯,这并不是.net中真正的漏洞。只是我认为封闭形式应该是死的,但不知何故,至少有一个来自外部世界的未知引用仍然与封闭形式联系在一起

我在网上读了一些文章。有人说,当MDI子窗体关闭时,我应该解除所有事件处理程序的锁定,否则某些事件处理程序可能会使窗体保持活动状态。有人说应该在表单关闭之前清理数据绑定,否则数据绑定将添加对某个全局哈希表的引用,从而使表单保持活动状态

我的表格里有很多东西。许多事件处理程序、许多数据绑定、许多BindingSources和少数可疑控件包含用户控件和HelpProvider。我创建了一个大方法,可以从所有相关控件中取消所有事件处理程序,清除所有数据绑定和数据源。HelpProvider和用户控件被小心地处理

最后,我发现,我不必清除数据绑定和数据源。事件处理程序肯定是问题的根源。MDI表单结构也有一定的作用

在我的实验中,我发现,如果创建MDI子窗体,即使关闭它,内存中仍会有一个实例。该引用来自主窗体的PropertyStore。这意味着,除非主窗体关闭(应用程序结束),否则内存中总会有一个MDI子窗体实例。好消息是,无论您打开和关闭子窗体多少次,都只有一个实例,而不是一个大的“泄漏”

当涉及到事件处理程序时,事情变得更加棘手。我必须解决这个问题,我表单上的所有事件处理程序都是匿名事件处理程序。下面是一个示例代码:

//On MDI child form's design code...

Button btnSave = new Button(); 

btnSave.Click += new System.EventHandler(btnSave_Click);
其中
btnSave\u Click
也是MDI子表单中的一种方法。上述情况始终适用于各种控件和各种类型的事件。对我来说,这是一个双向循环引用。btnSave通过事件处理程序保留MDI子窗体的引用。MDI子窗体保留btnSave实例的引用。对我来说,这种双向循环引用不应该给.net的垃圾收集器带来任何问题。这意味着,在处理表单时,我不必显式取消事件:

btnSave.Click -= btnSave_Click;
但事实并非如此。对于某些事件处理程序,它们是安全的。忽略它们不会导致任何重复实例。对于其他一些事件处理程序,它们将导致内存中剩余一个实例(与MDI表单结构的效果类似,但这次是由挂起的事件处理程序引起的)。对于其他一些事件处理程序,它们将导致在内存中打开每个实例。我完全搞不懂这三种类型的事件处理程序之间的区别。控件以相同的方式创建,事件以相同的方式附加。有什么区别?(不要告诉我是事件句柄方法起了作用。)有没有人有过这种有线场景的经验并能给我一个答案?非常感谢

所以现在,为了安全问题,我必须在处理表单时解除所有事件处理程序的锁定。对于每个控件,这将是一个类似代码的长列表。是否有一种使用反射以递归方式从控件中删除事件的通用方法?性能问题呢


这就是我的故事的结尾,我仍然处在我的问题的中间。感谢您的帮助。

只要事件处理程序连接到的对象在子窗体中声明,您就不必在释放子窗体时删除事件处理程序,但是,当事件处理程序连接到的对象在子窗体外部声明时,必须在释放子窗体时删除事件处理程序。

如果此代码执行多次(并且btnSave是未在子窗体中声明的对象)

此代码必须执行相同的次数

btnSave.Click -= btnSave_Click;


可能是您正在应用程序中的某个位置引用子窗体,在垃圾收集器删除子窗体对象之前,必须先删除此引用。

您的问题可以通过提供的解决方案解决。
btnSave.Click -= btnSave_Click;