我在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,而不是我自己的代码。
答案 0 :(得分:1)
好吧,我不是垃圾收集专家,但我知道两件事:
这些就是我对它了解不多的原因,我不需要了解太多。
除此之外,这里粗略地概述了垃圾收集的工作原理。
想象一下有这个代码:
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 中,并在处理完后立即将其删除。