为什么WeakReference.IsAlive变得虚假?

时间:2014-08-25 09:09:22

标签: c# .net garbage-collection finalizer

作为this question的后续内容,我有以下代码:

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 called, child: I am a child!, is alive: True
~Test starts
DoTest called, child: I am a child!, is alive: False
~Test ends
~Child called

问题:为什么WeakReference.IsAlive的{​​{1}}成为_child false,而~Test()对象仍然固定向下_child

1 个答案:

答案 0 :(得分:2)

好吧,我记得访问"类实例变量"从终结器中获取并不是一个好主意,因为它们可以随机使用#34;州? 这基本上意味着WeakReference终结器将在您的课程终结器之前调用。

  

有一个特殊的运行时线程专用于调用Finalize   方法。当可释放队列为空时(通常是   case),这个线程睡觉了。但是当条目出现时,这个线程会唤醒,   从队列中删除每个条目,并调用每个对象的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现在会报告错误。引用本身实际上是活着的。