析构函数的垃圾收集器行为

Garbage Collector Behavior for Destructor

我有一个简单的 class 定义如下。

public class Person
{
    public Person()
    {

    }

    public override string ToString()
    {
        return "I Still Exist!";
    }

    ~Person()
    {
        p = this;

    }
    public static Person p;
}

在 Main 方法中

    public static void Main(string[] args)
    {
        var x = new Person();
        x = null;

        GC.Collect();
        GC.WaitForPendingFinalizers();
        Console.WriteLine(Person.p == null);

    }

垃圾收集器是否应该作为 Person.p 的主要引用以及何时调用析构函数?

你在这里遗漏的是编译器将你的 x 变量的生命周期延长到定义它的方法结束 - 这只是编译器所做的事情 - 但它只对 DEBUG 构建有效。

如果您更改代码以便在单独的方法中定义变量,它将按您预期的那样工作。

以下代码的输出为:

False
True

代码:

using System;

namespace ConsoleApp1
{
    class Finalizable
    {
        ~Finalizable()
        {
            _extendMyLifetime = this;
        }

        public static bool LifetimeExtended => _extendMyLifetime != null;

        static Finalizable _extendMyLifetime;
    }

    class Program
    {
        public static void Main()
        {
            test();

            Console.WriteLine(Finalizable.LifetimeExtended); // False.

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

            Console.WriteLine(Finalizable.LifetimeExtended); // True.
        }

        static void test()
        {
            new Finalizable();
        }
    }
}

所以基本上您的理解是正确的,但是您不知道偷偷摸摸的编译器会让您的变量保持活动状态,直到 在您调用 GC.Collect() 之后 - 即使如果您明确将其设置为空!

正如我上面提到的,这只发生在 DEBUG 构建中 - 大概是这样您可以在调试到方法结束时检查局部变量的值(但这只是一个猜测!)。

原始代码在发布版本中确实按预期工作 - 因此以下代码输出 false, true 用于 RELEASE 版本和 false, false 用于 DEBUG 版本:

using System;

namespace ConsoleApp1
{
    class Finalizable
    {
        ~Finalizable()
        {
            _extendMyLifetime = this;
        }

        public static bool LifetimeExtended => _extendMyLifetime != null;

        static Finalizable _extendMyLifetime;
    }

    class Program
    {
        public static void Main()
        {
            new Finalizable();

            Console.WriteLine(Finalizable.LifetimeExtended); // False.

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

            Console.WriteLine(Finalizable.LifetimeExtended); // True iff RELEASE build.
        }
    }
}

作为附录:请注意,如果您在 class 的终结器中执行某些操作,导致对正在终结的对象的引用可以从程序根访问,那么该对象将不是垃圾-收集,除非并直到不再引用该对象。

换句话说,你可以通过终结器给一个对象一个"stay of execution"。不过,这通常被认为是一个糟糕的设计!

例如,在上面的代码中,我们在终结器中执行 _extendMyLifetime = this 的地方,我们正在创建一个对该对象的新引用,因此在 _extendMyLifetime 之前它不会被垃圾回收(和任何其他参考)不再引用它。