使用Object Initializer的复活差异

jer*_*ers 5 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);
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 17

编辑:虽然下面的原始答案仍然准确,看起来它是调试信息和优化的混合,这在这里有所作为.

从我的实验:

Compiler flags                        Result
/o+ /debug-                           Finalizer runs
/o+ /debug+                           Finalizer runs
/o- /debug-                           Finalizer runs
/o- /debug+                           Finalizer does *not* run
Run Code Online (Sandbox Code Playgroud)

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

现在使用对象初始化程序,编译器代码在堆栈上包含一个额外的引用.这一行:

Zombie z = new Zombie { Name = "Guy" };
Run Code Online (Sandbox Code Playgroud)

有效地:

Zombie tmp = new Zombe();
tmp.Name = "Guy";
Zombie z = tmp;
Run Code Online (Sandbox Code Playgroud)

z在设置了所有属性后才执行赋值.

我的猜测是tmp这里的变量是保持对象存活.