【问题标题】:MDI WinForm application and duplicate child form memory leak [duplicate]MDI WinForm 应用程序和重复子窗体内存泄漏 [重复]
【发布时间】:2009-07-14 00:14:45
【问题描述】:

这是一个 WinForm MDI 应用程序问题(.net framework 3.0)。它将在 C# 中进行描述。抱歉,有点长,因为我尽量把事情说清楚。

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

我在网上阅读了一些文章。有人说当 MDI 子窗体关闭时,我应该取消所有事件处理程序的连接,否则某些事件处理程序可能会使我的窗体保持活动状态。有人说应该在关闭表单之前清理 DataBindings,否则 DataBindings 将添加对某个全局 Hashtable 的引用,从而使我的表单保持活动状态。

我的表单包含很多东西。许多事件处理程序和许多 DataBindings 和许多 BindingSources 以及一些包含用户控件和 HelpProvider 的可疑控件。我创建了一个大方法,从所有相关控件中取消连接所有事件处理程序,清除所有 DataBindings 和 DataSources。 HelpProvider 和用户控件被小心处理。

最后,我发现,我不必清除 DataBindings 和 DataSources。事件处理程序肯定会导致问题。而且 MDI 的表单结构也有所贡献。

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

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

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

Button btnSave = new Button(); 

btnSave.Click += new System.EventHandler(btnSave_Click);

其中btnSave_Click也是MDI子窗体中的一个方法。对于各种控件和各种类型的事件,上述情况总是如此。对我来说,这是一个双向循环引用。 btnSave 通过事件处理程序保持对 MDI 子窗体的引用。 MDI 子窗体保留 btnSave 实例的引用。再次对我来说,这种双向循环引用不应该对.net 的垃圾收集器造成任何问题。这意味着我不必在处理表单时显式取消连接事件:

btnSave.Click -= btnSave_Click;

但事实并非如此。对于某些事件处理程序,它们是安全的。忽略它们不会导致任何重复实例。对于其他一些事件处理程序,它们会导致一个实例留在内存中(与MDI表单结构类似的效果,但这次是由挂起的事件处理程序引起的)。对于其他一些事件处理程序,它们将导致在内存中打开每个实例。我对这三种类型的事件处理程序之间的差异感到非常困惑。控件的创建方式相同,事件的附加方式相同。有什么区别? (不要告诉我是事件处理方法有所不同。)任何人都有这种有线场景的经验并且对我有答案吗?非常感谢。

所以现在,出于安全考虑,我必须在处理表单时取消所有事件处理程序的连接。这将是每个控件的一长串类似代码。是否有一种使用反射以递归方式从控件中删除事件的通用方法?性能问题呢?

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

【问题讨论】:

  • 您的问题可以通过here提供的解决方案来解决。

标签: winforms c#-3.0 mdi


【解决方案1】:

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

如果这段代码被执行了多次(并且 btnSave 是一个没有在子窗体中声明的对象)

btnSave.Click += btnSave_Click;

这段代码必须执行相同的次数

btnSave.Click -= btnSave_Click;


可能是您在应用程序的某个地方引用了子表单,必须先删除此引用,然后垃圾收集器才会删除子表单对象。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-12-05
    • 2017-10-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-22
    • 1970-01-01
    • 2016-06-04
    相关资源
    最近更新 更多