4

こんにちは私はここにブレークポイントに到達した理由がわからないコードがあります(コメントを参照)。

これは私が知らない、または正しく理解していない何かのマイクロソフトのバグですか?

コードはデバッグでテストされましたが、何も変更されるべきではないと思います。

注:コンソールアプリでコードを直接テストできます。

情報のためだけに...supercatの回答に続いて、提案されたソリューションでコードを修正しましたが、うまく機能します:-) !!! 悪いことは、静的dictの使用とそれに伴うパフォーマンスですが、機能します。...数分後、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が無効になることがあります。これは、コンストラクターのパラメーターがであった場合や、 のターゲットが強力にルート化されている場合でも発生する可能性があります。この動作は、 にアンマネージ リソース (GC ハンドル) があり、 のファイナライザーがGC ハンドルをクリーンアップしない場合、クリーンアップされず、メモリ リークが発生するという事実に起因します。WeakReferenceWeakReference trackResurrectiontrueWeakReferenceWeakReferenceWeakReference

オブジェクトのファイナライザーがWeakReferenceオブジェクトを使用する必要がある場合、オブジェクトはそれらのオブジェクトが強く参照されたままになるように何らかの準備をする必要があります。これを達成するための最良のパターンが何であるかはわかりませんがConditionalWeakTable<TKey,TValue>、.net 4.0 で追加された が役立つ場合があります。Dictionary<TKey,TValue>テーブル自体が強く参照され、特定のキーが強く参照されている限り、対応する値が強く参照されていると見なされることを除いて、少し似ています。ConditionalWeakTableaが X を Y にリンクし、Y をテーブルにリンクするエントリを保持している場合、X または Y が残っている限り、テーブルもそのままであることに注意してください。

于 2013-02-28T23:57:28.373 に答える
5

期待していなかったガベージ コレクションの側面が2 つあります。

  • WeakReference.IsAlive が false になる正確な時刻。あなたのコードは、ファイナライザーの実行時にそれが起こることを暗黙的に想定しています。これは当てはまりません。オブジェクトがガベージ コレクションされるときに発生します。その後、ファイナライザーがあり、GC.SuppressFinalize() が呼び出されず、ファイナライザー スレッドがそのジョブを実行するのを待っているため、オブジェクトはファイナライザー キューに配置されます。そのため、IsAlive が false である期間がありますが、~B() はまだ実行されていません。

  • オブジェクトがファイナライズされる順序は予測できません。B が A の前にファイナライズされると暗黙的に仮定します。この仮定を行うことはできません。

B.Dispose() メソッドにもバグがあり、クライアント コードが明示的にオブジェクトを破棄した場合、B インスタンスを正しくカウントしません。あなたはまだそのバグにぶつかっていません。

このコードを修正する合理的な方法はありません。さらに、CLR によって提供されるハード保証によって既に裏付けられているものをテストします。取り除くだけです。

于 2013-02-26T19:36:27.480 に答える
2

WeakReference_weakBは、オブジェクトと同時にガベージ コレクションに使用できますa。ここでは順序が保証されていないため、_weakBobject の前にファイナライズされる可能性が非常に高くなりますa

_weakBの状態がわからないため、 A のファイナライザーでのアクセスは危険です_weakB。あなたの場合、それはファイナライズされており、それが null に対して null を返す原因になっていると思います.Target

于 2013-02-26T16:36:44.723 に答える