为什么WeakReference.IsAlive变得虚假?

作为这个问题的后续,我有以下代码:

using System; using System.Runtime.InteropServices; namespace ConsoleApplication1 { class Program { class Child { public override string ToString() { return "I am a child!"; } ~Child() { Console.WriteLine("~Child called"); } } class Test { readonly object _child; readonly WeakReference _ref; readonly GCHandle _gch; // GCHandle is a value type, so it's safe public Test() { _child = new Child(); _ref = new WeakReference(_child); _gch = GCHandle.Alloc(_child); } // ... public void DoTest() { lock (_child) { Console.WriteLine("DoTest called, child: " + _child.ToString() + ", is alive: " + _ref.IsAlive); } } ~Test() { Console.WriteLine("~Test starts"); DoTest(); _gch.Free(); Console.WriteLine("~Test ends"); } } static void Main(string[] args) { var test = new Test(); test.DoTest(); test = null; GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); System.Threading.Thread.Sleep(1000); GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); Console.ReadLine(); } } } 

输出:

 DoTest打电话给,孩子:我还是个孩子!还活着:真的
 〜测试开始
 DoTest打电话给,孩子:我还是个孩子!,还活着:是的
 〜测试结束
 〜孩子叫

问题:为什么_child WeakReference.IsAlive~Test()变为false ,而_child对象仍然用GCHandle.Alloc固定下来?

好吧,我记得从终结器访问“类实例变量”并不是一个好主意,因为它们可能处于“随机”状态? 这基本上意味着WeakReference终结器将在您的类终结器之前调用。

有一个特殊的运行时线程专用于调用Finalize方法。 当可释放队列为空(通常是这种情况)时,该线程将hibernate。 但是当条目出现时,该线程会唤醒,从队列中删除每个条目,并调用每个对象的Finalize方法。 因此,您不应该在Finalize方法中执行任何代码,该方法对正在执行代码的线程做出任何假设。 例如,避免在Finalize方法中访问线程本地存储。

http://msdn.microsoft.com/en-us/magazine/bb985010.aspx

如果您确定了WeakReference,您可以获得更有意义的结果:

  public Test() { _child = new Child(); _ref = new WeakReference(_child); _gch = GCHandle.Alloc(_child); _test = GCHandle.Alloc(_ref); } 

如果让GC知道现在无法收集WeakReference类ITSELF ,您可以获得相同的结果,如下所示:

 static void Main(string[] args) { var test = new Test(); var win = new WeakReference(test._child); test._ref = win;//new WeakReference(test._child); test.DoTest(); test = null; } 

来自WeakReference的实际代码:

  ~WeakReference() { IntPtr old_handle = m_handle; if (old_handle != IntPtr.Zero) { if (old_handle == Interlocked.CompareExchange(ref m_handle, IntPtr.Zero, old_handle)) GCHandle.InternalFree(old_handle); } } 

你可以看到它在终结器运行后释放手柄,将其设置为零并且IsAlive现在将报告为假。 引用本身实际上是活着的。