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

public class Person
{
    public Person()
    {

    }

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

    ~Person()
    {
        p = this;

    }
    public static Person p;
}

在主要方法中
    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()之后使变量保持事件状态-即使您将其显式设置为null!

如上所述,这仅在DEBUG构建中发生-大概是这样,您可以在调试到方法末尾时检查局部变量的值(但这只是一个猜测!)。

原始代码确实可以在发行版本中正常工作-因此以下代码输出RELEASE版本的false, true和DEBUG版本的false, false:
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.
        }
    }
}

作为附录:请注意,如果您在类的终结器中执行某项操作,从而导致可以从程序根目录访问对要终结的对象的引用,则除非并且直到该对象不再存在,否则该对象将不会被垃圾回收。引用。

换句话说,您可以通过终结器为对象赋予“执行中止”。但是,通常认为这是一个不好的设计!

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

关于c# - 析构函数的垃圾收集器行为,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/58376457/

10-17 00:12