Why WeakReference.IsAlive becomes false?

删除回忆录丶 提交于 2020-01-14 14:46:09

问题


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

The 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: why does WeakReference.IsAlive for _child become false inside ~Test(), while the _child object is still pinned down with GCHandle.Alloc?


回答1:


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

There is a special runtime thread dedicated to calling Finalize methods. When the freachable queue is empty (which is usually the case), this thread sleeps. But when entries appear, this thread wakes, removes each entry from the queue, and calls each object's Finalize method. Because of this, you should not execute any code in a Finalize method that makes any assumption about the thread that's executing the code. For example, avoid accessing thread local storage in the Finalize method.

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

If you pin down 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 WeakReference class ITSELF can't be collected for 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;
}

The 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 releases the handle once it's finalizer has been run, sets it zero & IsAlive will report false now. The reference itself is actually alive though.



来源:https://stackoverflow.com/questions/25482453/why-weakreference-isalive-becomes-false

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!