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


9

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

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的主要引用,以及何时确切调用析构函数?


首先:C#中的析构函数被视为终结器。第二:将单例实例设置为即将完成的实例似乎是一个非常非常糟糕的主意。第三:什么Person1?我只看到Person。最后:有关终结器的工作方式,请参阅docs.microsoft.com/dotnet/csharp/programming-guide/…
HimBromBeere

@HimBromBeere Person1实际上是Person,修正了输入错误。
Parimal Raj

@HimBromBeere这实际上是一个面试问题,根据我的理解,现在CG.Collect应该调用了析构函数,但没有。
Parimal Raj

2
(1)如果您在其finalizer中重新引用要最终确定的对象,则将无法对其进行垃圾收集,直到无法从根目录访问该引用为止(因此这会延迟其垃圾收集的效果)。(2)调用终结器的时间点是不可预测的。
马修·沃森

@HimBromBeere,当我在Console.WriteLine Person.p处设置断点时,无论GC.Collect调用如何,它都为null
Parimal Raj

Answers:


13

您在这里缺少的是,编译器将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构建中发生-大概是这样,您可以在调试到方法末尾时检查局部变量的值(但这只是一个猜测!)。

原始代码确实可以在发行版本中正常工作-因此,以下代码输出false, true用于RELEASE版本和false, falseDEBUG版本:

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(和任何其他引用)不再引用该对象之前,将不再对其进行垃圾收集。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.