【问题标题】:Delegate variables not garbage collected委托变量不被垃圾收集
【发布时间】:2010-01-21 00:03:58
【问题描述】:

最近发现 ToGadget 中的变量,可能还有委托,没有被垃圾收集。任何人都可以看到为什么 .NET 持有对此的引用?似乎在 Foo 结束后,委托和所有人都将被标记为垃圾收集。从字面上看,在转储堆后,内存中有 B百万个。

注意:'result.Things' 是一个 List (),Converter 是一个系统委托。


        public Blah Foo()
        {
                var result = new Blah();
                result.Things = this.Things.ConvertAll((new Converter(ToGadget)));
                return result;
        }
        .................
        public static Gadget ToGadget(Widget w)
        {
            return new Gadget(w);
        }

更新:将“ConvertAll”更改为此清除委托和相应的对象引用。这向我表明,要么 List ConvertAll 以某种方式保留了委托,要么我不明白这些东西是如何被垃圾收集的。


            foreach (var t in this.Things)
            {
                result.Things.Add(ToGadget(t));         
            }

【问题讨论】:

  • 该代码中的委托到底是什么?
  • Blindee:Converter的构造函数中的ToGadget。
  • 那不是很清楚的代码。什么是转换器,什么是 Blah?
  • 您的代码没有在 ToGadget 中引入任何变量,因此对 GC 没有任何影响...您能给我们一个真实的、可重现的示例(或一些真实的代码)吗?
  • 如果这是一个旨在重现您遇到的问题的简化示例,您可以简化问题。

标签: c# .net memory-management delegates garbage-collection


【解决方案1】:

使用内存分析器。

您可以整天在 StackOverflow 上提问,得到一堆有根据的猜测,或者您可以在您的应用程序上安装一个内存分析器,然后立即查看什么是 root 以及什么是垃圾。有一些工具专门用于快速轻松地解决您的确切问题。使用它们!

【讨论】:

  • 他正在“转储堆”并查看特定实例这一事实是否表明他已经在进行某种形式的内存分析?我将他的问题更多地解读为“为什么我的分析告诉我我有 XXXX?这没有意义。”
  • 我看过很多没有分析器的堆转储,所以,不,这并不意味着我正在执行分析。
【解决方案2】:

您的问题存在一个重大缺陷,这可能是造成混淆的原因:

似乎委托和所有将在 Foo 结束后被标记为垃圾收集。

CLR 不会在例程结束时为收集“标记项目”。相反,一旦该例程结束,就不再有对您的委托中引用的任何项目的(活动)引用。那时,它们就是所谓的“无根”。

稍后,当 CLR 确定有一定的内存压力时,垃圾收集器就会执行。它将搜索并找到所有无根元素,并可能收集它们。

这里的重要区别是时间不是可以预测的。在您的程序结束之前,这些对象可能永远不会被收集,或者它们可能会立即被收集。由系统决定何时收集。这不会在 Foo 结束时发生 - 而是在 Foo 结束后的某个未知时间发生。


编辑:

顺便说一句,这实际上是直接解决您的问题。您可以通过强制垃圾回收来查看这是否是问题所在。只需在调用 Foo 之后添加一个调用:

GC.Collect();
GC.WaitForPendingFinalizers();

然后检查 CLR 的堆。此时,如果您仍然在堆中获取对象,那是因为对象仍然被某些东西所植根。您的简化示例没有显示发生这种情况,但由于这是一个非常简化的示例,因此很难确定这会发生在哪里。 (注意:如果是这种情况,我不建议将其保留在您的代码中。手动调用 GC.Collect() 几乎总是一个坏主意...)

【讨论】:

  • 感谢您的澄清,但对于确定为什么内存中有这么多对象并没有真正帮助
  • 嗯——这实际上直接解决了这个问题。我会编辑得更清楚...
  • 内存中有这么多对象,因为垃圾收集器还没有觉得有必要进行一次完整的传递并清理它们。
  • @Anon:这正是我的想法。我认为这可能是对 GC 工作原理的误解,而不是对象有问题...
  • 有趣的想法。但是当你有一个 9GB 的进程时,为什么垃圾收集器看不到需要做 pass 呢?似乎不太可能,但我可能是错的。
【解决方案3】:

看起来您的函数已设置为返回新的 Blah()。它实际上是在您的代码中返回的吗?我在您发布的文章中看到它不是。如果是这种情况,那么 new Blah() 将具有 Foo 之外的范围,并且它可能是实际上将引用保存在范围内的调用函数。此外,您也在创建 new Gadget()。根据您拥有的小工具 Blah 的数量,您可能会以指数方式填充您的内存,因为小工具的范围将与 Blah 一起被限定,然后将其保存在 Foo 之外的范围内。

不管我是对是错,这种可能性打起来很有趣。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-25
    • 1970-01-01
    • 2019-02-22
    • 2011-05-07
    相关资源
    最近更新 更多