我有一个简单的类,定义如下。
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/