【问题标题】:Garbarge collector order垃圾收集器订单
【发布时间】:2017-03-31 08:54:49
【问题描述】:

我知道 .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 对象的顺序不是确定性的,而是 如果仍然有一些 B 对象具有对 a_obj 的引用并且还没有被销毁,那么垃圾收集器怎么可能销毁 a_obj?

【问题讨论】:

  • 并发模式下的GC在单独的线程中运行,也许只是写入控制台的延迟?顺序是否一致?
  • 必须这样做;否则任何两个垃圾对象都可以通过相互引用来保持彼此永远活着;基本上,一旦对象不是rooted(可通过真实代码访问),那么它们就是垃圾;那时,垃圾内部的任何引用都是不相关并被忽略的。

标签: c# .net garbage-collection dispose


【解决方案1】:

.NET 垃圾收集器不使用引用计数,因此有多少对a_obj 的引用并不重要。重要的是可以从根中访问哪些对象(如静态变量)。在您的情况下,所有对象(A 和所有 Bs)同时变得无法从 root 访问,因此都可以进行垃圾收集,它们的相互引用无关紧要。如果AB 都无法访问并且将在同一个循环中被收集,那么为什么还要考虑发生这种情况的顺序呢?此外,如果顺序取决于哪个对象引用了哪个 - 考虑一下它将如何收集相互引用的对象?

这也是来自documentation 的引用,它完全符合您的情况:

不保证两个对象的终结器在任何 特定的顺序,即使一个对象引用另一个对象。也就是说,如果 对象 A 具有对对象 B 的引用,并且都具有终结器 Object 当对象 A 的终结器时,B 可能已经被终结 开始。

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

【讨论】:

  • 这是有道理的。那么我该如何处理这个问题呢?
  • 究竟是哪个问题?我在这里没有看到任何问题。
【解决方案2】:

如果引用a_obj的元素都没有被引用,GC可以收集它,因为应用程序根没有办法再次获得对它的引用。

这里是a good tutorial on C# object lifecycle的相关部分

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

【讨论】:

    【解决方案3】:

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

    【讨论】:

      猜你喜欢
      • 2018-12-30
      • 1970-01-01
      • 2011-11-07
      • 2013-04-01
      • 2012-06-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多