Dispatcher的PriorityQueue导致“内存泄漏”

时间:2013-01-24 22:55:25

标签: c# wpf listview memory-leaks virtualization

我在WPF中构建的UI框架中遇到垃圾收集问题。我在引号中使用内存泄漏,因为我认为我理解这个问题但不是解决方案或某种解决方法。

当我创建UIElements但不显示它们时会发生此问题。从UI的角度来看,我使用的是虚拟化列表视图(GridView),因此不会立即显示所有行。在我的一个用例中,我创建了一个Export Rows功能,可以将所有行导出到csv。通常,行中的单元格由字符串或整数组成。

它变得棘手的是一些单元格本身是UIElements,这是列表虚拟化的原因之一(制作一些的成本很耗时并且可能是内存耗费)所以我只在创建UIElements时才会然后我甚至不缓存它们(使它们有资格收集)。

当我通过行提供程序枚举我访问属性时,问题变为,创建并返回UIElements,从中提取数据,写入csv,然后查看下一行。在这个循环中没有对这些Rows(ViewModel类)的引用,所以人们会认为它们有资格进行垃圾收集。情况似乎并非如此。使用.Net Memory Profiler我推断出我的“Out of Memory Exceptions”是由于PriorityQueue(来自Dispatcher)中的DispatcherOperation将UIElements保留在内存中的结果。我认为这是因为他们等待显示(但从来没有)。

如果我没有得到内存不足异常,那么这些UIElements似乎是通过PriorityQueue处理的(我猜它放弃了)并且是垃圾收集的。这是最好的情况。当我处理少量行时,这似乎没什么问题,但是当我们进入50,000到100k级别时,这是另一个故事。我可以向您保证,对于这些ViewModel或UIElements本身(在DispatcherOperation之外)没有其他引用。

有关如何解决此问题或解决此问题的任何想法?无论如何阻止这些未显示的排队,很快就会被废弃的UIElements?

编辑2013年1月25日: 我意识到我可能需要特别清理内存中保存的内容。行对象,因为它们没有对UIElement的引用,所以没有保存在内存中(这很好)。只有通过Get访问器访问的UIElements才会在内存中存在。对它们的唯一引用是PriorityQueue,而不是我自己的代码。

1 个答案:

答案 0 :(得分:1)

好吧,我不是垃圾收集专家,但我知道两件事:

  1. 您几乎不需要关心垃圾收集
  2. 您几乎不需要手动进行垃圾收集

这些就是我对它了解不多的原因,我不需要了解太多。

除此之外,这里粗略地概述了垃圾收集的工作原理。

想象一下有这个代码:

class Program
{
    class Something
    {
        public string name { get; set; }
    }


    class Container
    {
        List<Something> myList = new List<Something>();

        public void AddNewSomething()
        {
            Something mySomething = new Something() { name = "test" };
            myList.Add(mySomething);
        }
    }

    public static void Main(string[] args)
    {
        Container myContainer = new Container();
        myContainer.AddNewSomething();

        while (true)
        {
            Console.WriteLine("Something will always be in Memory");
        }
    }
}

垃圾收集是关于被引用的对象。所以看看方法:AddNewSomething。方法完成后是否需要局部变量mySomething?该对象可以被垃圾收集吗?答案是否定的,因为对象 mySomething 现在被 myList 引用。

由于在 Main 中存在无限循环 myList 不能被垃圾回收,并且由于它包含一个 MySomething 对象,所以该对象也不能被垃圾回收,否则它会消失从列表中。

有意义吗?我希望如此。

鉴于 UIElements,我相信它们总是有一个父级?一个容纳它们的容器。该父级通常是一个具有子级列表的对象,就像在我的示例中一样。因此,除非父级不能被垃圾收集器处理,因为有对它的引用(例如,它需要显示在窗口中),否则垃圾收集器不能处理子级。因此,您添加的 UIElements 越多,孩子的列表就会变得越来越大。您必须主动将它们从父级中删除。有一些代码丢失,所以我不能给出一个明确的例子,但你正在寻找类似的东西:

someParent.Children.Remove(noLongerRequiredUiElement);

但除此之外,为什么要创建和添加从未显示的 UIElements?看起来您只是在处理一些数据,这些数据可以驻留在与 UI 完全无关的单独类中。鉴于你的问题,我并不完全清楚你为什么这样做。但我认为你可能会再次考虑你的类架构。这样您就可以将项目放在 Queue 中,并在处理完后立即将其删除。