【问题标题】:Memory leak and local variables内存泄漏和局部变量
【发布时间】:2012-01-29 02:46:18
【问题描述】:

我有以下方法(在棱镜的 RegionAdapter 中)。

protected override void Adapt(IRegion region, DocumentPane regionTarget)
{
    region.Views.CollectionChanged += delegate(object sender, NotifyCollectionChangedEventArgs e)
    {
        OnViewsCollectionChanged(sender, e, region, regionTarget);
    };
}

我的问题是通过这种方式订阅事件,我有内存泄漏。但是 region 和 regionTarget 参数是 Adapt 的局部变量。

我尝试查找此内容以查看是否有人以不同方式处理该场景,但 MSDN 和 MVP 都使用这种泄漏方法...

我应该如何处理它以便我以后可以取消订阅该事件?

编辑:
上面的代码只是一个例子,我认为清楚地说明了问题。
实际泄漏内存的代码如下:

private void OnViewsCollectionChanged(object sender, 
    NotifyCollectionChangedEventArgs e, IRegion region, 
    DocumentPane regionTarget)
{
    if (e.Action == NotifyCollectionChangedAction.Add)
    {
        //Add content panes for each associated view.
        foreach (object item in e.NewItems)
        {
            UIElement view = item as UIElement;

            if (view != null)
            {
                DockableContent newContentPane = new DockableContent();
                newContentPane.IsCloseable = true;
                newContentPane.HideOnClose = false;

                ScrollViewer sViewer = new ScrollViewer()
                {
                    Content = item,
                    HorizontalScrollBarVisibility = ScrollBarVisibility.Auto,
                    VerticalScrollBarVisibility = ScrollBarVisibility.Auto
                };
                newContentPane.Content = sViewer;

                //When contentPane is closed remove the associated region
                // (MEMORY LEAK)
                newContentPane.Closed += (contentPaneSender, args) =>
                {
                    DockableContent docker =
                        contentPaneSender as DockableContent;
                    ScrollViewer scroller = docker.Content as ScrollViewer;
                    region.Remove(scroller.Content);
                    IDisposable dispView = scroller.Content as IDisposable;
                    if (dispView != null) dispView.Dispose();
                    scroller.Content = null;
                };
                regionTarget.Items.Add(newContentPane);
                newContentPane.Activate();
            }
        }
    }
}

谢谢,

巴布。

【问题讨论】:

  • 为什么你认为它会泄漏内存?只要Views 实例仍然被引用,包含Adapt 方法的类的实例就会留在内存中。当region 被收集时,你的Adapter 也会被收集。您的 region 是对静态实例的引用吗?
  • 如果您将OnViewsCollectionChanged 方法设为静态(如果可能),它会阻止Adapter 被引用。
  • 实际上,这是重现问题的最微小的一点。最常用的区域注入了无限量的视图。我将更新问题以显示导致严重内存泄漏的原因,但我想您理解不是取消订阅 = 内存泄漏。
  • @Steven 所以如果我做对了,如果我把 Closed 事件处理中使用的匿名方法的内容放在一个常规的静态方法中,我确保不会有内存泄漏?跨度>
  • 没错。 C# 编译器将生成一个带有regionregionTarget 作为公共字段的匿名类型,当OnViewsCollectionChanged 是实例方法时,该匿名类型还将包含对持有OnViewsCollectionChanged 的实例的引用。当OnViewsCollectionChanged 为静态时,该匿名类型将不包含对该实例的引用,因此该实例将不会保持活动状态。您可以使用 Reflector 确认这一点。

标签: c# memory-leaks delegates


【解决方案1】:

使用具体方法而不是匿名。例如:

   private Region _region;
   DocumentPane _regionTarget
   protected override void Adapt(IRegion region, DocumentPane regionTarget)
    {
        _region = region;
        _regionTarget = regionTarget;
        region.Views.CollectionChanged += OnCollectionChanged;
    }

    private void OnCollectionChanged (object sender, NotifyCollectionChangedEventArgs e) 
    {
        OnViewsCollectionChanged(sender, e, _region, _regionTarget);
    }

    ...
    private void Unsubscribe() 
    {
      _region.Views.CollectionChanged -= OnCollectionChanged;
    }

【讨论】:

  • 您的代码将无法编译:region 和 regionTarget 是 OnCollectionChanged 方法的未知变量。它们是 Adapt 本地的。
  • 我想过,但现在这两个变量不再是本地的,它们是全局的。我敢肯定必须有另一种方法,也许对我不知道的代表使用技巧......不过感谢您的意见。
  • 删除匿名方法不会改变任何事情。该实例仍将被引用。另一方面,取消订阅确实可以解决问题。
  • 是的,但是如果不将变量移到方法之外,我怎么能做到呢?
猜你喜欢
  • 1970-01-01
  • 2023-03-28
  • 2019-04-11
  • 2014-05-25
  • 2013-07-06
  • 1970-01-01
  • 2013-07-21
  • 2013-11-06
  • 1970-01-01
相关资源
最近更新 更多