4

嗨,我在这里有代码,我不明白我为什么要打断点(见评论)。

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

该代码在 Debug 中进行了测试,但我认为它不应该改变任何东西。

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

仅供参考...在 supercat 回答之后,我用建议的解决方案修复了我的代码,它运行良好:-) !!! 坏事是静态字典的使用以及随之而来的性能,但它确实有效。...几分钟后,我意识到 SuperCat 给了我所有提示,让我做得更好,解决静态字典,我做到了。代码示例是:

  1. 有错误的代码
  2. 代码已更正但使用静态 ConditionalWeakTable
  3. 包含 SuperCat 技巧的 ConditioalWeakTable 代码(非常感谢他!)

样品...

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

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

包含 SuperCat 技巧的 ConditioalWeakTable 代码(非常感谢他!)

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

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

}
4

3 回答 3

6

有时令人讨厌的限制是,如果不存在对自身WeakReference的强根引用,则 a可能会失效,即使构造函数参数是,并且即使 的目标是强根的,也可能会发生这种情况。这种行为源于 a有一个非托管资源(一个 GC 句柄),如果 的终结器没有清理 GC 句柄,它将永远不会被清理并会构成内存泄漏。WeakReferenceWeakReference trackResurrectiontrueWeakReferenceWeakReferenceWeakReference

如果对象的终结器有必要使用WeakReference对象,则该对象必须做出一些规定以确保这些对象保持强引用。我不确定实现此目的的最佳模式是什么,但ConditionalWeakTable<TKey,TValue>在 .net 4.0 中添加的模式可能有用。有点像Dictionary<TKey,TValue>,只不过只要一个表本身被强引用,给定的key被强引用,其对应的值就会被认为是强引用。请注意,如果 aConditionalWeakTable包含一个将 X 链接到 Y 并将 Y 链接到 table 的条目,那么只要 X 或 Y 保留,则 table 也将保留。

于 2013-02-28T23:57:28.373 回答
5

垃圾收集有两个方面是您没有指望的:

  • WeakReference.IsAlive 变为假的确切时间。您的代码隐含地假设终结器运行时会发生这种情况。情况并非如此,它发生在对象被垃圾收集时。之后对象被放置在终结器队列中,因为它有终结器并且 GC.SuppressFinalize() 没有被调用,等待终结器线程完成它的工作。所以有一段时间 IsAlive 为假但 ~B() 尚未运行。

  • 对象最终确定的顺序是不可预测的。您隐含地假设 B 在 A 之前完成。您不能做出这个假设。

B.Dispose() 方法中还有一个错误,当客户端代码显式处置对象时,它不会正确计算 B 实例。你还没有遇到那个错误。

没有合理的方法来修复此代码。此外,它测试已经由 CLR 提供的硬保证支持的东西。只需将其删除。

于 2013-02-26T19:36:27.480 回答
2

WeakReference_weakB与对象同时可用于垃圾收集a。您在这里没有订单保证,因此很可能_weakB是在 object 之前完成的a

在 A 的终结器中访问_weakB是危险的,因为您不知道_weakB. 我猜在你的情况下它已经完成,这导致它返回 null for .Target

于 2013-02-26T16:36:44.723 回答