【问题标题】:Memory leak when using anonymous delegate with Loaded/Unloaded Event使用带有加载/卸载事件的匿名委托时的内存泄漏
【发布时间】:2013-10-09 06:27:22
【问题描述】:

我有一个 WPF MainWindow,可以在 StackPanel 中更改(导航)UserControl:

// on code behind of MainWindow     
RootStackPanel.Children.Clear();

                UserControl1 uc1= new UserControl1();
                uc1.CustomizedEvent1+= EventHandler1;
                uc1.CustomizedEvent2+= EventHandler2;
                uc1.Loaded += (s, e1) =>
                 {
                        // Do something
                 };

                // Unsubscribe external event to prevent memory leak
                uc1.Unloaded += (s, e1) =>
                    {
                        uc1.CustomizedEvent1 -= EventHandler1;
                        uc1.CustomizedEvent2 -= EventHandler2;
                    };
RootStackPanel.Children.Add(uc1);

// same for UserControl 2,3,4....

应用程序将在运行几个小时后崩溃,所以我添加了带有匿名委托的 Unloaded 事件处理程序,以通过取消订阅 UserControl 的所有事件来防止内存泄漏。 Unloaded with Anonymous Delegate 会导致内存泄漏吗?如果是,如何在不调用 Unloaded 的情况下取消订阅 UserControl 的所有事件?

我记得像 Loaded, Unloaded 这样的内部事件会被 GC 处理掉吧?

匿名代表何时处置?

谢谢大家。

【问题讨论】:

  • 只有在没有其他对象订阅 Unload 事件时,CG 才会收集用户控件。
  • 嗨@PanagiotisLefas,所以我的解决方案很糟糕,因为 Unload 的匿名委托仍保留在 MainWindow 上,并且 GC 无法收集 UserControl。您对此有任何替代解决方案吗?谢谢
  • @QuocNguyen 你的解决方案很好

标签: wpf memory-leaks user-controls


【解决方案1】:

您说“应用程序在运行几个小时后会崩溃,所以我添加了 Unloaded 事件”——这是否意味着您在 StackPanel 中经常更改(添加和删除)UserControld?此外,每次创建新的 UserControl 时,都会从 MainWindow 添加这些事件处理程序,对吗?

在这种情况下,您一切正常,将匿名委托分配给 Unloaded 事件以进行清理是留下良好内存足迹的正确方法。

【讨论】:

  • 感谢您的回复。
  • 别忘了标记答案。如果您有任何其他问题,请随时提出。或者打开另一个线程。
  • 您说“应用程序在运行几个小时后会崩溃,所以我添加了 Unloaded 事件”——这是否意味着您在 StackPanel 中经常更改(添加和删除)UserControld? ** 是 **此外,每次您创建新的 UserControl 时,您都会从 MainWindow 添加这些事件处理程序,对吗? 是的。我和你一样想,但我不知道 Unloaded 事件何时释放/取消订阅?用户报告说我的应用程序在工作几个小时后被冻结。那么我们是否有任何替代解决方案来取消订阅用户控制的所有事件?
  • 调用 children.Remove(myUserControl) 时会卸载 UserControl。如果您不相信我,请在您的匿名方法中设置断点。 :) :) 在您的情况下,匿名方法不包含对其他类的任何引用,因此一旦完整的 UserControl 被丢弃,它将由 gc 收集。当您调用 children.Remove(..) 时会发生这种情况
  • 我的队友说匿名委托是一个静态方法,只有在关闭MainWindow时才会被释放。你能确认一下这个信息吗?
【解决方案2】:

我找到了一个简单的解决方案来解决这个问题:

  • 使用 EventHandler 方法代替匿名委托。
  • 在 EventHandler 方法中调用取消订阅。

    .....
    // on code behind of MainWindow     
    RootStackPanel.Children.Clear();
    
            UserControl1 uc1= new UserControl1();
            uc1.CustomizedEvent1+= EventHandler1;
            uc1.CustomizedEvent2+= EventHandler2;               
    
            // Unsubscribe external event to prevent memory leak
            //uc1.Unloaded += (s, e1) =>
             //   {
             //       uc1.CustomizedEvent1 -= EventHandler1;
             //       uc1.CustomizedEvent2 -= EventHandler2;
             //   };
    RoutedEventHandler unloadedHandler = null;
    unloadedHandler = delegate(object s, RoutedEventArgs sev)
        {
     uc1.CustomizedEvent1 -= EventHandler1;
             uc1.CustomizedEvent2 -= EventHandler2;
    uc1.Unloaded -= unloadedHandler;
    };
        uc1.Unloaded += unloadedHandler;
    RootStackPanel.Children.Add(uc1);
    // same for UserControl 2,3,4....
    

【讨论】:

  • 再次,您不需要取消订阅 Unloaded 事件。当 UserControl 从子列表中删除时,GC 将看到 UserControl 和您的匿名事件处理程序都单独丢失在空间中,它会同时收集两者。
猜你喜欢
  • 1970-01-01
  • 2011-07-07
  • 1970-01-01
  • 1970-01-01
  • 2021-04-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多