C#的WeakReference对象是在终结NULL虽然依然强烈引用(C# WeakReferenc

2019-07-20 13:38发布

您好我这里有代码,我不明白为什么我打了断点(见注释)。

这是什么我不知道或我不正确地理解微软的错误?

该代码在调试测试,但我认为它不应该改变任何东西。

注意:您可以在一个控制台应用程序直接测试代码。

JUST FOR信息...以下supercat答案,我定我的代码与提出的解决方案,它很好地工作:-)! 坏事是一个静态字典的使用和性能去与它,但它的工作原理。 ......几分钟后,我意识到,SuperCat给我所有的提示做的更好,要解决静态字典,我做到了。 代码示例如下:

  1. 与错误代码
  2. 代码更正,但有一个静态ConditionalWeakTable
  3. 与ConditioalWeakTable代码,包括SuperCat技巧(非常感谢他!)

样本...

using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace WeakrefBug
{

// **********************************************************************
class B : IDisposable
{
    public static List<B> AllBs = new List<B>();

    public B()
    {
        AllBs.Add(this);
    }

    private bool disposed = false;
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            AllBs.Remove(this);
            disposed = true;
        }
    }

    ~B() { Dispose(false); }
}

// **********************************************************************
class A
{
    WeakReference _weakB = new WeakReference(new B());

    ~A()
    {
        B b = _weakB.Target as B;
        if (b == null)
        {
            if (B.AllBs.Count == 1)
            {
                Debugger.Break(); // b Is still referenced but my weak reference can't find it, why ?
            }
        }
        else { b.Dispose(); }
    }
}

// **********************************************************************
class Program
{
    static void Main(string[] args)
    {
        A a = new A();
        a = null;

        GC.Collect(GC.MaxGeneration);
        GC.WaitForPendingFinalizers();
    }
    }

    // **********************************************************************
}

版本修正:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace WeakrefBug // Working fine with ConditionalWeakTable
{
    // **********************************************************************
    class B : IDisposable
    {
        public static List<B> AllBs = new List<B>();

        public B()
        {
            AllBs.Add(this);
        }

        private bool disposed = false;
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!disposed)
            {
                AllBs.Remove(this);
                disposed = true;
            }
        }

        ~B() { Dispose(false); }
    }

    // **********************************************************************
    class A
    {
        private static readonly System.Runtime.CompilerServices.ConditionalWeakTable<A, B> WeakBs = new ConditionalWeakTable<A, B>();

        public A()
        {
            WeakBs.Add(this, new B());          
        }

        public B CreateNewB()
        {
            B b = new B();
            WeakBs.Remove(this);
            WeakBs.Add(this, b);
            return b;
        }

        ~A()
        {
            B b;
            WeakBs.TryGetValue(this, out b);

            if (b == null)
            {
                if (B.AllBs.Count == 1)
                {
                    Debugger.Break(); // B Is still referenced but my weak reference can't find it, why ?
                }
            }
            else { b.Dispose(); }
        }
    }

    // **********************************************************************
    class Program
    {
        static void Main(string[] args)
        {
            A a = new A();
            WeakReference weakB = new WeakReference(a.CreateNewB()); // Usually don't need the internal value, but only to ensure proper functionnality
            a = null;

            GC.Collect(GC.MaxGeneration);
            GC.WaitForPendingFinalizers();

            Debug.Assert(!weakB.IsAlive);
        }
    }

    // **********************************************************************
}

与ConditioalWeakTable代码,包括SuperCat技巧(非常感谢他!)

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace WeakrefBug // Working fine with non static ConditionalWeakTable - auto cleanup
{
    // **********************************************************************
    class B : IDisposable
    {
        public static List<B> AllBs = new List<B>();

        public B()
        {
            AllBs.Add(this);
        }

        private bool disposed = false;
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!disposed)
            {
                AllBs.Remove(this);
                disposed = true;
            }
        }

        ~B() { Dispose(false); }
    }

    // **********************************************************************
    class A
    {
        private ConditionalWeakTable<object, object> _weakBs = null;

        public A()
        {
        }

        public B CreateNewB()
        {
            B b = new B();
            if (_weakBs == null)
            {
                _weakBs = new ConditionalWeakTable<object, object>();
                _weakBs.Add(b, _weakBs);
            }
            _weakBs.Remove(this);
            _weakBs.Add(this, b);
            return b;
        }

        internal ConditionalWeakTable<object, object> ConditionalWeakTable // TestOnly
        {
            get { return _weakBs; }
        }

        ~A()
        {
            object objB;
            _weakBs.TryGetValue(this, out objB);

            if (objB == null)
            {
                if (B.AllBs.Count == 1)
                {
                    Debugger.Break(); // B Is still referenced but my weak reference can't find it, why ?
                }
            }
            else
            {
                ((B)objB).Dispose();
            }
        }
    }

    // **********************************************************************
    class Program
    {
        static void Main(string[] args)
        {
            A a = new A();
            WeakReference weakB = new WeakReference(a.CreateNewB()); // Usually don't need the internal value, but only to ensure proper functionnality
            WeakReference weakConditionalWeakTable = new WeakReference(a.ConditionalWeakTable);
            a = null;

            GC.Collect(GC.MaxGeneration);
            GC.WaitForPendingFinalizers();

            Debug.Assert(!weakB.IsAlive);
            Debug.Assert(!weakConditionalWeakTable.IsAlive);
        }
    }

    // **********************************************************************

}

继CitizenInsane的问题......我不记得究竟为什么我做了什么,我没有...我发现我的样本,但不知道对我当时的用意。 我试图弄明白,并用下面的代码,我的事情更清晰,但仍然不记得我原来需要来了。 对不起???

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace WeakrefBug // Working fine with ConditionalWeakTable
{
    // **********************************************************************
    class B : IDisposable
    {
        public static List<B> AllBs = new List<B>();

        public B()
        {
            AllBs.Add(this);
        }

        private bool disposed = false;
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!disposed)
            {
                AllBs.Remove(this);
                disposed = true;
            }
        }

        ~B() { Dispose(false); }
    }

    // **********************************************************************
    class A
    {
        private ConditionalWeakTable<object, object> _weakBs = null;
        private WeakReference _weakB = null;

        public A()
        {
            _weakBs = new ConditionalWeakTable<object, object>();
            B b = new B();
            _weakB = new WeakReference(b);
            _weakBs.Add(b, _weakB);
        }

        public B B
        {
            get
            {
                return _weakB.Target as B;
            }
            set { _weakB.Target = value; }
        }

        internal ConditionalWeakTable<object, object> ConditionalWeakTable // TestOnly
        {
            get { return _weakBs; }
        }

        ~A()
        {
            B objB = B;

            if (objB == null)
            {
                if (B.AllBs.Count == 1)
                {
                    Debugger.Break(); // B Is still referenced but my weak reference can't find it, why ?
                }
            }
            else
            {
                ((B)objB).Dispose();
            }
        }
    }

    // **********************************************************************
    class Program
    {
        static void Main(string[] args)
        {
            Test1();
            Test2();
        }

        private static void Test1()
        {
            A a = new A();
            WeakReference weakB = new WeakReference(a.B); // Usually don't need the internal value, but only to ensure proper functionnality
            WeakReference weakConditionalWeakTable = new WeakReference(a.ConditionalWeakTable);

            a = null;

            GC.Collect(GC.MaxGeneration);
            GC.WaitForPendingFinalizers();

            Debug.Assert(B.AllBs.Count == 0);

            GC.Collect(GC.MaxGeneration);
            GC.WaitForPendingFinalizers();

            Debug.Assert(!weakB.IsAlive); // Need  second pass of Collection to be collected
            Debug.Assert(!weakConditionalWeakTable.IsAlive);
        }

        private static void Test2()
        {
            A a = new A();
            WeakReference weakB = new WeakReference(a.B);

            B.AllBs.Clear();
            a.B = null;

            GC.Collect(GC.MaxGeneration);
            GC.WaitForPendingFinalizers();

            Debug.Assert(!weakB.IsAlive); // Need  second pass of Collection to be collected
        }
    }

    // **********************************************************************

}

Answer 1:

的有时-令人厌烦的限制WeakReference是一个WeakReference如果如果没有强烈层次参考存在于可能无效WeakReference 本身 ,这即使可能发生trackResurrection构造参数是true ,并且即使目标WeakReference强烈扎根 。 这种现象从一个事实茎WeakReference有非托管资源(GC手柄),如果该终结WeakReference没有清理GC手柄,它永远不会被清理,将构成一个内存泄漏。

如果有必要为对象的终结尽量使用WeakReference对象,该对象必须作出一些规定,以确保这些对象仍然坚决引用。 我不知道最好的方式是实现这个东西,但ConditionalWeakTable<TKey,TValue>这是在.NET 4.0中添加的可能是有用的。 这是一个有点像Dictionary<TKey,TValue>不同之处在于只要一个表本身强烈引用和给定的关键是大力引用,其相应的价值将被视为强烈引用。 请注意,如果ConditionalWeakTable持有的条目链接X到Y,和Y表,然后只要保持X或Y,该表将保持为好。



Answer 2:

有垃圾收集,你没有指望的两个方面:

  • 确切的时间,让WeakReference.IsAlive变为假。 您的代码隐含地假定终结器运行时会发生。 这是不是这样,当对象获取垃圾回收它发生。 在这之后的对象放在终结队列,因为它有一个终结和GC.SuppressFinalize()不叫,等待终结器线程来完成其工作。 因此,有一段时间,其中的IsAlive是假的,但〜B()尚未运行。

  • 在哪些对象得到最终确定的顺序是不可预测的。 您隐含假设B之前A.你不能做这样的假设最终确定。

还有在B.Dispose()方法中的错误,也不会正确计数乙情况下,当客户端代码明确地设置的对象。 您还没有打这个bug呢。

还有就是要解决这个代码没有合理的方式。 此外,它测试的东西,已经由CLR提供强有力的保证支持。 只是将其删除。



Answer 3:

该WeakReference的_weakB可用于垃圾收集作为处理对象,同时a是。 你没有顺序保证在这里,所以它很可能是_weakB的对象之前敲定a

访问_weakB在A的终结是危险的,因为你不知道的状态_weakB 。 我猜你的情况已经敲定,而这导致它的返回null .Target



文章来源: C# WeakReference object is NULL in finalizer although still strongly referenced