c# - 使用Object Initializer的复活区别

标签 c# garbage-collection finalizer

我有这个代码:

本质上,我试图演示 c# 终结器的使用,并制作一个不会死亡的对象,我将其称为 Zombie。现在,通常这个演示效果很好,但今天我尝试使用相同的代码和对象初始值设定项,而不是仅仅分配给属性(在本例中为 Name)。我注意到有区别。也就是说,永远不会调用终结器,即使我正在尽最大努力让垃圾收集器完成它的工作。

有人可以解释一下区别吗,或者我是否发现了 C# 编译器中的错误?

(我在 Win7x64 上的 VS2010 SP1 中使用 C# 4)

谢谢。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace Zombie
{
  class Program
  {
    static void Main(string[] args)
    {
      Console.WriteLine("Main thread: " + Thread.CurrentThread.ManagedThreadId);

              // case 1: this is where the problem is located.
      Zombie z = new Zombie { Name = "Guy" }; // object initializer syntax makes that the finalizer is not called.

              // case 2: this is not causing a problem. The finalizer gets called.
      //Zombie z = new Zombie();
      //z.Name = "Guy";

      WeakReference weakZombieGuyRef = new WeakReference(z, true);

      z = null;

      GC.GetTotalMemory(forceFullCollection: true);

      GC.Collect();

      while (true)
      {

        Console.ReadKey();
        if (weakZombieGuyRef.IsAlive)
        {
          Console.WriteLine("zombie guy still alive");
        }
        else
        {
          Console.WriteLine("Zombie guy died.. silver bullet anyone?");
        }

        Zombie.Instance = null;

        GC.AddMemoryPressure(12400000);
        GC.GetTotalMemory(forceFullCollection: true);

        GC.Collect();
      }


    }
  }

  public class Zombie
  {
    public string Name { get; set; }
    public  static Zombie Instance = null;

    ~Zombie()
    {
      Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
      Console.WriteLine("Finalizer called on zombie" + this.Name);
      lock (typeof(Zombie))
      {
        Instance = this;

        GC.ReRegisterForFinalize(this);
      }
    }
  }
}

最佳答案

编辑:虽然下面的原始答案仍然准确,但看起来调试信息和优化的混合使这里有所不同。

根据我的实验:

Compiler flags                        Result
/o+ /debug-                           Finalizer runs
/o+ /debug+                           Finalizer runs
/o- /debug-                           Finalizer runs
/o- /debug+                           Finalizer does *not* run

当在命令行上使用 /o+ 进行编译时,终结器仍会在我的机器上调用。我的猜测是您正在调试器中运行 - 这会改变 GC 行为。如果没有调试器,GC 将收集它可以证明永远不会被读取的任何内容。 使用调试器,我相信 GC 不会收集任何在堆栈上仍有引用的对象,即使没有代码来读取有问题的变量也是如此。 p>

现在有了对象初始值设定项,编译器代码在堆栈上包含一个额外的引用。这一行:

Zombie z = new Zombie { Name = "Guy" };

是有效的:

Zombie tmp = new Zombe();
tmp.Name = "Guy";
Zombie z = tmp;

z 的赋值仅在所有属性都已设置之后执行。

我的猜测是这里的 tmp 变量使对象保持事件状态。

关于c# - 使用Object Initializer的复活区别,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9753256/

相关文章:

java - 为什么枚举常量没有 finalize 方法?

c# - 有没有一种简单的方法可以将序数数字字符串转换为其匹配的数值?

c# - Docker容器中的.Net Core 3.1应用程序环境变量不起作用

c# - NamedScope 和垃圾回收

c# - GC.Collect() 没有立即收集?

java - 终结器导致可移植性问题 - 怎么办?

c# - 从 Nunit 获取失败测试列表

c# - 设置 Cursor.Position "half"- 有效(从一台显示器到另一台显示器)

.net - 循环和垃圾收集

java - 我们应该总是释放 Java 中的 Map 或 List 吗?