.net垃圾收集器是否收集了整个对象图?

时间:2018-10-20 16:12:33

标签: .net garbage-collection

可以说,对象A唯一引用对象B,而对象B唯一引用对象C。如果ObjectA不再具有对它的引用,那么当ObjectA被垃圾收集时,ObjectB和ObjectC也会被垃圾收集吗?

3 个答案:

答案 0 :(得分:4)

这取决于您希望答案的准确性。

首先,当一个对象不再具有任何引用它的根引用时,该对象就被称为“符合收集条件”。

“根引用”是通过任何源自“根”的路径的引用,该“根”最终指向对象。一种这样的根类型是appdomain中的所有静态字段,另一种是仍然在调用堆栈上的方法上的局部变量,只要这些方法仍可用于此引用。

让我们举个例子。

对象A表示B,B表示C。不考虑其他引用。

从A到B的引用被删除,而B没有根引用。同样,C也没有根目录引用,因为无法通过其他对象从根目录转到C。

这样,当您从A删除对B的引用时,B和C现在都有资格收集

但是,不一定必须立即收集它们,也不必将它们作为同一收集周期的一部分进行收集。

非常简化,.NET内存管理系统是围绕3个不同的堆(从第0代到第2代)构建的。还有一个“大对象堆”,但这对于此答案并不是真正必要的。生成第0代是最初构造所有对象的地方,当它填满时,将执行第0代的垃圾回收周期,分析哪些对象仍然存在,哪些不是。

所有已生根的对象都将保留并成为第一代(下一代)的一部分,所有不再具有任何生根引用的对象都将被收集。带有终结器的对象不遵循此规则,但这再次与此处不相关。

第1代填满时,它也会被收集,将尚存的对象移入第2代。

不一定要同时收集第0、1和2代。在需要生成第1代之前,您可以具有第0代的多个集合。

因此,如果B和C是不同世代的一部分,则不会,它们不一定同时收集。

他们完成,但是同时成为有资格收藏

答案 1 :(得分:1)

是的

不要将垃圾收集器视为正在寻找没有任何引用的对象。可以认为这是使 do 具有引用的对象保持活动状态。

基本上:分配新对象时,内存从何而来?内存中没有对象仍处于活动状态的任何位置(也就是说,可以通过运行代码以某种方式使用该对象)。无法再运行代码无法到达的任何内存部分,GC将使用无法访问的内存空间来分配新对象。

(现代GC还会在内存中移动对象,以使其更易于分配新对象,但这超出了此答案的范围。)

答案 2 :(得分:1)

例如:

class Foo
{
    public string Name { get; set; } = "...";
    public object Ref { get; set; }

    ~Foo()
    {
        Console.WriteLine($"{Name} is finalized.");
    }
}

class Program
{
    static void Main(string[] args)
    {
        var objectA = new Foo
        {
            Name = "Object A",
            Ref = new Foo
            {
                Name = "Object B",
                Ref = new Foo
                {
                    Name = "Object C"
                }
            }
        };
        Console.WriteLine("Begin");
        objectA = null;
        GC.Collect();
        GC.WaitForPendingFinalizers();
        Console.WriteLine("End");
        Console.ReadKey();
    }
}

输出:

Begin
Object C is finalized.
Object B is finalized.
Object A is finalized.
End

其他事实是参考循环。如果对象A引用对象B引用对象C引用对象A:

class Program
{
    static void Main(string[] args)
    {
        var objectA = new Foo
        {
            Name = "Object A",
            Ref = new Foo
            {
                Name = "Object B",
                Ref = new Foo
                {
                    Name = "Object C"
                }
            }
        };
        ((Foo)((Foo) objectA.Ref).Ref).Ref = objectA;
        Console.WriteLine("Begin");
        Console.WriteLine(objectA.Name);
        objectA = null;
        GC.Collect();
        GC.WaitForPendingFinalizers();
        Console.WriteLine("End");
    }
}

输出:

Begin
Object C is finalized.
Object B is finalized.
Object A is finalized.
End

您需要了解图形对象的开始是App Domain。在最后一个示例中:

App Domain -> Object A -> Object B -> Object C -|
               ↑----------------------------|
相关问题