c# - 调度程序的 PriorityQueue 导致 "Memory Leaks"

标签 c# wpf listview memory-leaks virtualization

我在 WPF 中构建的 UI 框架中遇到垃圾收集问题。我在引号中使用内存泄漏,因为我认为我理解这个问题,但没有解决方案或某种解决方法。
当我创建 UIElements 但不显示它们时会出现此问题。从 UI 的角度来看,我使用的是虚拟化 ListView (GridView)。所以所有的行不会一次显示。在我的一个用例中,我创建了一个导出行功能,该功能能够将所有行导出到 csv。通常,行中的单元格由字符串或整数等基元组成。
棘手的地方在于,有些单元格本身就是 UIElements,这也是列表被虚拟化的原因之一(制作一些单元格的成本非常耗时,而且可能会占用大量内存)。所以我只在询问时创建 UIElements,然后我什至不缓存它们(使它们有资格被收集)。
当我通过行提供程序进行枚举时访问属性时会出现此问题。创建并返回 UIElements,从中提取数据,写入 csv,然后查看下一行。在这个循环中,没有对这些行(ViewModel 类)的引用,因此人们会认为它们有资格进行垃圾收集。情况似乎并非如此。使用 .Net Memory Profiler,我推断出我的“内存不足异常”是 UIElements 被 PriorityQueue(来自 Dispatcher)中的 DispatcherOperation 保留在内存中的结果。我认为这是因为它们正在等待显示(但永远不会显示)。
如果我没有遇到内存不足异常,最终这些 UIElements 似乎是通过 PriorityQueue 处理的(我猜它放弃了)并且被垃圾收集。这是最好的情况。当我处理少量行时,这似乎很好。但是当我们进入 50,000 - 100k 级别时,又是另一回事了。我可以向您保证,没有其他对这些 ViewModel 或 UIElements 本身的引用(在 DispatcherOperation 之外)。
关于如何解决此问题或解决此问题的任何想法?有没有办法阻止这些未显示的、即将被使用的 UIElements 的排队?
2013 年 1 月 25 日编辑:
我意识到我可能需要专门清除内存中保存的内容。行对象,因为它们没有对 UIElement 的引用,它们没有保存在内存中(这很好)。只有通过 Get 访问器访问的 UIElements 会留在内存中。对它们的唯一引用是 PriorityQueue 而不是我自己的代码。

最佳答案

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

  • 你几乎不需要关心垃圾收集
  • 您几乎不需要手动进行垃圾收集

  • 这些就是我对它了解不多的原因,我也不需要了解太多。
    除此之外,这里是垃圾收集如何工作的粗略概述。
    想象一下有这个代码:
    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,我相信他们总是有一个 parent ?一个容纳它们的容器。该父级通常是一个具有子级列表的对象,就像在我的示例中一样。因此,除非父级不能被垃圾收集器处理,因为有对它的引用(例如,它需要显示在窗口中),否则垃圾收集器不能处理子级。因此,您添加的 UIElements 越多, child 的列表就会变得越来越大。您必须主动将它们从父级中删除。有一些代码丢失,所以我不能给出一个明确的例子,但你正在寻找这样的东西:
    someParent.Children.Remove(noLongerRequiredUiElement);
    
    但除此之外,为什么要创建和添加从未显示的 UIElements?看起来您只是在处理一些数据,这些数据可以驻留在与 UI 完全无关的单独类中。鉴于你的问题,我并不完全清楚你为什么这样做。但我认为你可能会再次考虑你的类架构。这样你就可以在 Queue 中拥有项目,并在处理后立即将它们一一删除。

    关于c# - 调度程序的 PriorityQueue 导致 "Memory Leaks",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14512374/

    相关文章:

    c# - TextBox文本作为参数n Double事件

    android - 如何在作为抽屉导航一部分的 fragment 中创建 ListView

    android - 附有页脚的 ListView,smoothScrollToPosition 无法正常工作

    c# - asp.net 核心 Razor 页面 'loading indicator'

    c# - c#语言中的事件处理程序

    c# - 检测修改键按下?

    wpf - 如何将 Microsoft Edge WebView2 Runtime 添加为先决条件

    android - 实现弹出菜单 ListView

    c# - 绑定(bind)到后面代码中的相关源

    c# - 开发人员是否可以通过 "force"系统来防止 "Metro"应用程序在后台异步进程运行时被挂起?