Garbarge收藏家订单

时间:2017-03-31 08:54:49

标签: c# .net garbage-collection dispose

我知道.Net垃圾收集器在销毁对象时无法保证任何顺序,但我对以下行为感到非常惊讶:

using System;
using System.Collections.Generic;

namespace ConsoleApplication2 {
class Program {

    class A {
        public A() { i = 1; }
        private int i;

        // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
        ~A() {
            // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
            Console.WriteLine("Disposed A");
        }
    }

    class B {
        static int p = 0;
        public B(A a) {
            this.a = a;
            j = p++;
        }

        ~B() {
            Console.WriteLine("Disposed " + j.ToString());
        }

        private int j;
        private A a;
    }

    static void Main(string[] args) {
        A a_obj = new A();
        List<B> bs = new List<B>();
        for (int i = 0; i < 10; i++) {
            B b = new B(a_obj);
            bs.Add(b);
        }
    }
}
}

此执行具有以下(可能)输出:

处理9

处理2

处理1

处理0

处理A

处理8

处理7

处理6

处理5

处理4

处理3

我可以理解B对象的顺序不是确定性的 垃圾收集器如何破坏a_obj,如果还有一些B对象引用了a_obj并且还没有被销毁?

3 个答案:

答案 0 :(得分:3)

.NET垃圾收集器不使用引用计数,因此对a_obj的引用数量并不重要。重要的是从根可以访问哪些对象(比如静态变量)。在您的情况下,所有对象(A和所有B)同时无法从root访问,因此所有对象都有资格进行垃圾回收,它们的相互引用无关紧要。如果AB都无法访问并且将在同一周期中收集,那么为什么还要关注这种情况会发生什么?此外,如果顺序依赖于哪个对象引用哪个 - 想想它将如何收集彼此相互引用的对象?

以下是documentation的引用,它完全适合您的情况:

  

两个对象的终结器不能保证在任何对象中运行   特定顺序,即使一个对象引用另一个对象。也就是说,如果   对象A具有对象B的引用,并且都具有终结符Object   在对象A的终结器时,B可能已经完成   启动。

另请注意,首先所有终结器都将运行,然后只有对象才会真正死亡,并且它们占用的内存可能会被重用(您甚至可以通过GC.ReRegisterForFinalize重新确定终结器中的对象)。因此,您可以从A的终结器中访问B,即使A的终结器可能已经在此时运行。显然你应该避免这样做。

答案 1 :(得分:1)

如果没有引用引用a_obj的元素,GC可以收集它,因为应用程序root无法再次获取对它的引用。

以下是a good tutorial on C# object lifecycle

的相关部分

在垃圾收集过程中,运行时将调查托管堆上的对象,以确定它们是否仍可由应用程序访问(root)。为此, CLR将构建一个对象图,它表示堆上的每个可到达对象。对象图用于记录所有可到达的对象。 [...] 构建图形后,无法访问的对象被标记为垃圾。

答案 2 :(得分:0)

当进程关闭时产生输出,此时GC正在清理处理过程中分配的整个内存,对象之间的引用不会影响清理顺序。