【问题标题】:Memory leak on CollectionView.View.RefreshCollectionView.View.Refresh 上的内存泄漏
【发布时间】:2011-01-26 18:45:31
【问题描述】:

我已经这样定义了我的绑定:

<TreeView
        ItemsSource="{Binding UsersView.View}"
        ItemTemplate="{StaticResource MyDataTemplate}"
/>

CollectionViewSource 是这样定义的:

private ObservableCollection<UserData> users;
public CollectionViewSource UsersView{get;set;}
UsersView=new CollectionViewSource{Source=users};
UsersView.SortDescriptions.Add(
          new SortDescription("IsLoggedOn",ListSortDirection.Descending);
UsersView.SortDescriptions.Add(
          new SortDescription("Username",ListSortDirection.Ascending);

到目前为止,一切顺利,按预期工作:视图首先显示按字母顺序登录的用户,然后是未登录的用户。

但是,UserData 的 IsLoggedIn 属性每隔几秒由后台工作线程更新一次,然后代码调用:

UsersView.View.Refresh();

在 UI 线程上。

这再次按预期工作:登录的用户从视图的底部移动到顶部,反之亦然。但是:每次我在视图上调用 Refresh 方法时,应用程序都会囤积 3.5MB 的额外内存,这些内存仅在应用程序关闭后(或 OutOfMemoryException...)才会释放

我做了一些研究,以下是无效的修复列表:

  • UserData 类实现 INotifyPropertyChanged
  • 更改底层用户集合根本没有任何区别:任何 IENumerable&lt;UserData> 作为 CollectionViewSource 的源都会导致问题。
    - 将 ColletionViewSource 更改为 List&lt;UserData>(并刷新绑定)或从 ObservableCollection 继承以访问底层 Items 集合以对其进行就地排序不起作用。

我没有想法!帮忙?

编辑: 我找到了: Resource MyDataTemplate 包含一个绑定到 UserData 对象以显示其属性之一的 Label,UserData 对象由 TreeView 的 ItemsSource 传递。 Label 有一个这样定义的 ContextMenu:

 <ContextMenu Background="Transparent" Width="325" Opacity=".8" HasDropShadow="True">

      <PrivateMessengerUI:MyUserData IsReadOnly="True" >

          <PrivateMessengerUI:MyUserData.DataContext>

              <Binding Path="."/>

          </PrivateMessengerUI:MyUserData.DataContext>

     </PrivateMessengerUI:MyUserData>

</ContextMenu>

MyUserData 对象是一个显示 UserData 对象所有属性的 UserControl。这样用户首先只能看到一个用户的一条数据,然后右键点击就可以看到全部。

当我从 DataTemplate 中删除 MyUserData UserControl 时,内存泄漏消失了!我怎样才能仍然实现上面指定的行为?

【问题讨论】:

  • 您可以发布您的编辑作为答案并接受。
  • 您还在寻找内存泄漏的原因吗?你能发送重现泄漏的“POC”吗?
  • @UserFriendly:我还是想知道,是的。现在我只有在得到 OutOfMemoryExceptions 后才发现它。我想先预防一下 ;-)(NOOB 警报:)什么是 POC?
  • :) 好的,POC 是概念证明。证明任何想法的东西。在您的情况下,POC 是一个简单的应用程序,内部存在内存泄漏:)

标签: c# .net wpf data-binding memory-leaks


【解决方案1】:

你可以尝试两件事:

首先,DropShadow 存在一些问题,因为它包含一些应该被垃圾收集的引用。有关更多信息,请参阅本文: http://blog.ramondeklein.nl/index.php/2009/02/20/memory-leak-with-wpf-resources-in-rare-cases/

你可以尝试将 HasDropShadow 设置为 false,看看你的记忆会发生什么。

其次,如果我们有同一个菜单的多个 ContextMenu 对象,您可能希望在 Resources 中创建一个 ContextMenu 资源,并使用 StaticResource 扩展来引用它,如下所示:

<ContextMenu Background="Transparent" Width="325" Opacity=".8" x:Key="MyAwesomeContextMenu">

      <PrivateMessengerUI:MyUserData IsReadOnly="True" >

          <PrivateMessengerUI:MyUserData.DataContext>

              <Binding Path="."/>

          </PrivateMessengerUI:MyUserData.DataContext>

     </PrivateMessengerUI:MyUserData>

</ContextMenu>

并在定义上下文菜单的地方使用它:

ContextMenu="{StaticResource MyAwesomeContextMenu}"

希望这会有所帮助!

【讨论】:

  • 谢谢!阴影不是原因。它似乎真的是用户控件。我将其更改为 Stackpanel 并将其保存为资源。内存泄漏已经消失,只有我现在有重复的 xaml...
【解决方案2】:

解决内存泄漏问题的第一步是找到确定的来源。这并不总是显而易见的,有时会违背你的直觉。根据您的解释,如果您删除用户控件,问题就会消失,但是当您放回去时,您会再次开始泄漏。这很可能指向该控件内的内存泄漏(尽管不一定)。也许您的控件适合 one of the many types of WPF memory leaks,或者您有一个更经典的问题,即订阅事件但在不再需要时无法正确取消连接(the weak event pattern 在这里很有用)。

最好的办法是使用.NET Memory ProfilerANTS Memory Profiler 之类的工具(两者都非常好,并且可以免费试用)。使用其中一种工具找到应该消失后仍然存在的物体。这些工具有助于跟踪挂在对象上的对象链。 their sites 上有很多关于内存分析的好文章,这里是 SO 和广泛开放的网络。

【讨论】:

    猜你喜欢
    • 2012-12-23
    • 2017-02-28
    • 2017-04-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多