使用Object Initializer的复活差异

时间:2012-03-17 20:03:42

标签: 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);
      }
    }
  }
}

2 个答案:

答案 0 :(得分:16)

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

从我的实验中:

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不会收集仍然有堆栈引用的任何对象,即使没有代码来读取有问题的变量。

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

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

实际上是:

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

只有设置了所有属性后才能执行z的分配。

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

答案 1 :(得分:2)

如果你想要不死的物体,你真的不需要弄乱终结者。只需拥有一个包含所有实例的私有静态列表,并在创建它们时将对象添加到该列表中:

class Immortal
{
    static List<Immortal> _immortals = new List<Immortal>();

    public Immortal()
    {
       _immortals.Add(this);
    }
}