代码之家  ›  专栏  ›  技术社区  ›  Steve

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

  •  1
  • Steve  · 技术社区  · 16 年前

    这是一个WinForm MDI应用程序问题(.NET Framework 3.0)。它将在C中描述。对不起,时间有点长,因为我尽量把事情弄清楚。

    我有一个MDI应用程序。在某个时刻,我发现一个MDI子窗体永远不会被释放。有一个菜单可以创建并显示MDI子窗体。当MDI子窗体关闭时,它应该被销毁,它所占用的内存应该返回到.NET。但令我惊讶的是,这不是真的。所有MDI子窗体实例都保存在内存中。这显然是“内存泄漏”。好吧,这不是.NET真正的漏洞。只是我认为封闭形式应该是死的,但不知何故,至少有一个来自外部世界的未知引用仍然与封闭形式联系在一起。

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

    我的表格里有很多东西。许多事件处理程序、许多数据绑定、许多绑定源和一些包含用户控件和帮助提供程序的可疑控件。我创建了一个大方法,它将所有事件处理程序从所有相关控件中展开,清除所有数据绑定和数据源。帮助提供程序和用户控件被小心地释放。

    最后,我发现,不必清除数据绑定和数据源。事件处理程序肯定是导致问题的原因。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窗体结构,但这次是由挂起的事件处理程序引起的)。对于其他一些事件处理程序,它们将导致在内存中打开每个实例。对于这三种类型的事件处理程序之间的区别,我完全感到困惑。控件的创建方式与事件的附加方式相同。有什么区别?(不要告诉我是事件处理方法起了作用。)有没有人有过这种有线场景的经验,并为我提供了答案?谢谢。

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

    这就是我故事的结尾,我仍然处在问题的中心。有什么需要帮忙的,我谢谢。

    2 回复  |  直到 9 年前
        1
  •  1
  •   Community CDub    8 年前

    你的问题可以通过提供的解决方案来解决 here .

        2
  •  0
  •   Jens Granlund    15 年前

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

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

    btnSave.Click += btnSave_Click;

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

    btnSave.Click -= btnSave_Click;


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

    推荐文章