委托变量不是垃圾收集

时间:2010-01-21 00:03:58

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

最近发现ToGadget中的变量,也可能是委托,也没有收集垃圾。任何人都可以看到为什么.NET持有这个参考?似乎Foo结束后代表和所有代码都会被标记为垃圾收集。在转储堆后,直接在内存中看到了 B 的错误。

注意:'result.Things'是List< Gadget> ()和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”更改为清除代理和相应的对象引用。这向我建议列出<> ConvertAll以某种方式持有代表,或者我不明白这些东西是如何被垃圾收集的。


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

3 个答案:

答案 0 :(得分:6)

使用内存分析器。

你可以整天询问StackOverflow并获得一堆有根据的猜测,或者你可以在你的应用程序上打一个内存探查器,并立即看到什么是root,什么是垃圾。有专门为快速,轻松地解决您的确切问题而构建的工具。使用它们!

答案 1 :(得分:4)

你的问题有一个主要缺陷,可能是引起混淆的原因:

  

似乎Foo结束后代表和所有代码都会被标记为垃圾收集。

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

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

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


编辑:

这实际上是直接解决你的问题,顺便说一下。您可以通过强制执行垃圾回收来查看是否存在此问题。在致电Foo之后,只需添加一个电话:

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

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

答案 2 :(得分:2)

看起来你的函数设置为返回新的Blah()。它实际上是在您的代码中返回的吗?我在你发布的文章中看到它不是。如果是这种情况,那么新的Blah()将具有Foo之外的范围,并且它可能是实际持有范围中的引用的调用函数。此外,您还要创建新的Gadget()。根据你拥有的Blahs到Gadgets的数量,你可能会以指数方式填补你的记忆,因为Gadgets将使用Blahs作为范围,然后将其保留在Foo之外。

无论我是对还是错,这种可能性都很有趣。