Why is WeakReference.IsAlive getting false?

As a follow-up to this question , I have the following code:

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 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();
        }
    }
}

      

Output:

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

The question is: why is WeakReference.IsAlive

for _child

getting false

inside ~Test()

while the object _child

is still attached to GCHandle.Alloc

?

+3


source to share


1 answer


Well, I remember that accessing "class instance variables" from a finalizer is not a good idea as they might be in a "random" state? This basically means that the WeakReference finalizer will be called before your class's finalizer.

There is a special run-time thread dedicated to calling Finalize methods. When a free queue is empty (which is usually the case), this thread sleeps. But when records appear, this thread wakes up, removes each record from the queue, and calls each object's Finalize method. Because of this, you shouldn't be executing any code in the Finalize method that makes any assumption about the thread executing the code. For example, avoid accessing local thread storage in the Finalizer.

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

If you bind your WeakReference, you can get more meaningful results:

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

    }

      



You can get the same results if you let the GC know that the WeakReference ITSELF class cannot be compiled now, as such:

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;
}

      

Actual code from 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);
            }
        }

      

You can see that it frees the handle after the finalizer runs, sets it to zero, and IsAlive now reports an error. The link itself is really alive.

+2


source







All Articles