C#WeakReference的对象是终结NULL虽然依然强烈引用 [英] C# WeakReference object is NULL in finalizer although still strongly referenced

查看:1478
本文介绍了C#WeakReference的对象是终结NULL虽然依然强烈引用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



这是东西微软的bug我不知道或者我没有正确理解?



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



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



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




  1. 代码的bug

  2. 代码更正,但有一个静态ConditionalWeakTable

  3. 代码ConditioalWeakTable包括该SuperCat技巧(非常感谢他!)



样品。 ..

 使用系统; 
使用System.Collections.Generic;使用System.Diagnostics程序
;

命名空间WeakrefBug
{

// ******** *********
级A:IDisposable的
{
公共静态列表< b> AllBs =新的List< B>();

公众B()
{
AllBs.Add(本);
}

私人BOOL处置= FALSE;
公共无效的Dispose()
{
的Dispose(真);
GC.SuppressFinalize(本);
}

受保护的虚拟无效的Dispose(BOOL处置)
{
如果(!处置)
{
AllBs.Remove(本) ;
处置= TRUE;
}
}

〜B(){处置(假); }
}

// ********* **************
等级A
{
的WeakReference _weakB =新的WeakReference(新b());

〜A()
{
B B = _weakB.Target为B;
如果(二== NULL)
{
如果(B.AllBs.Count == 1)
{
Debugger.Break(); // B是仍然被引用,但我弱引用无法找到它,为什么呢?
}
}
,否则{b.Dispose(); }
}
}

// **** ************
类节目
{
静态无效的主要(字串[] args)
{
A A =新的A();
A = NULL;

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

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

版本修正:

 使用系统; 
使用System.Collections.Generic;使用System.Diagnostics程序
;使用System.Runtime.CompilerServices
;

命名空间WeakrefBug //做工精细用ConditionalWeakTable
{
// *********************** ***********************************************
类A:IDisposable的
{
公共静态列表< b> AllBs =新的List< B>();

公众B()
{
AllBs.Add(本);
}

私人BOOL处置= FALSE;
公共无效的Dispose()
{
的Dispose(真);
GC.SuppressFinalize(本);
}

受保护的虚拟无效的Dispose(BOOL处置)
{
如果(!处置)
{
AllBs.Remove(本) ;
处置= TRUE;
}
}

〜B(){处置(假); }
}

// ********* **************
等级A
{
私人静态只读System.Runtime.CompilerServices.ConditionalWeakTable< A,b> WeakBs =新ConditionalWeakTable< A,B>();

公开发行A()
{
WeakBs.Add(这一点,新的B());
}

公众B CreateNewB()
{
B B =新的B();
WeakBs.Remove(本);
WeakBs.Add(这一点,B);
回复B;
}

〜A()
{
B B;
WeakBs.TryGetValue(这一点,OUT B);

如果(二== NULL)
{
如果(B.AllBs.Count == 1)
{
Debugger.Break(); // B是仍然被引用,但我弱引用无法找到它,为什么呢?
}
}
,否则{b.Dispose(); }
}
}

// **** ************
类节目
{
静态无效的主要(字串[] args)
{
A A =新的A();
的WeakReference weakB =新的WeakReference(a.CreateNewB()); //通常不需要内在价值,而只是以确保适当的functionnality
A = NULL;

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

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

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

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

 使用系统; 
使用System.Collections.Generic;使用System.Diagnostics程序
;使用System.Runtime.CompilerServices
;

命名空间WeakrefBug //做工精细与非静态ConditionalWeakTable - 自动清理
{
// ****************** ************************************************** **
b类:IDisposable的
{
公共静态列表< b> AllBs =新的List< B>();

公众B()
{
AllBs.Add(本);
}

私人BOOL处置= FALSE;
公共无效的Dispose()
{
的Dispose(真);
GC.SuppressFinalize(本);
}

受保护的虚拟无效的Dispose(BOOL处置)
{
如果(!处置)
{
AllBs.Remove(本) ;
处置= TRUE;
}
}

〜B(){处置(假); }
}

// ********* **************
等级A
{
私人ConditionalWeakTable<对象,对象> _weakBs = NULL;

公开发行A()
{
}

公众B CreateNewB()
{
B B =新B( );
如果(_weakBs == NULL)
{
_weakBs =新ConditionalWeakTable<对象,对象>();
_weakBs.Add(B,_weakBs);
}
_weakBs.Remove(本);
_weakBs.Add(这一点,B);
回复B;
}

内部ConditionalWeakTable<对象,对象> ConditionalWeakTable // TestOnly
{
{返回_weakBs; }
}

〜A()
{
对象objB;
_weakBs.TryGetValue(这一点,出objB);

如果(objB == NULL)
{
如果(B.AllBs.Count == 1)
{
Debugger.Break(); // B是仍然被引用,但我弱引用无法找到它,为什么呢?
}
}
,否则
{
((B)objB).Dispose();
}
}
}

// ******** *********
级计划
{
静态无效的主要(字串[] args)
{
A A =新的A();
的WeakReference weakB =新的WeakReference(a.CreateNewB()); //通常不需要内在价值,而只是以确保适当的functionnality
的WeakReference weakConditionalWeakTable =新的WeakReference(a.ConditionalWeakTable);
A = NULL;

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

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

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

$} b $ b

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



 使用系统; 
使用System.Collections.Generic;使用System.Diagnostics程序
;使用System.Runtime.CompilerServices
;

命名空间WeakrefBug //做工精细用ConditionalWeakTable
{
// *********************** ***********************************************
类A:IDisposable的
{
公共静态列表< b> AllBs =新的List< B>();

公众B()
{
AllBs.Add(本);
}

私人BOOL处置= FALSE;
公共无效的Dispose()
{
的Dispose(真);
GC.SuppressFinalize(本);
}

受保护的虚拟无效的Dispose(BOOL处置)
{
如果(!处置)
{
AllBs.Remove(本) ;
处置= TRUE;
}
}

〜B(){处置(假); }
}

// ********* **************
等级A
{
私人ConditionalWeakTable<对象,对象> _weakBs = NULL;
私人的WeakReference _weakB = NULL;

公开发行A()
{
_weakBs =新ConditionalWeakTable<对象,对象>();
B B =新的B();
_weakB =新的WeakReference(B);
_weakBs.Add(B,_weakB);
}

公众B B
{
得到
{
返回_weakB.Target为B;
}
集合{_weakB.Target =价值; }
}

内部ConditionalWeakTable<对象,对象> ConditionalWeakTable // TestOnly
{
{返回_weakBs; }
}

〜A()
{$ B $条b B objB = B;

如果(objB == NULL)
{
如果(B.AllBs.Count == 1)
{
Debugger.Break(); // B是仍然被引用,但我弱引用无法找到它,为什么呢?
}
}
,否则
{
((B)objB).Dispose();
}
}
}

// ******** *********
级计划
{
静态无效的主要(字串[] args)
{
Test1的();
的Test2();
}

私有静态无效的Test1()
{
A A =新的A();
的WeakReference weakB =新的WeakReference(A·B); //通常不需要内在价值,而只是以确保适当的functionnality
的WeakReference weakConditionalWeakTable =新的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!); //需要收集第二次被收集
Debug.Assert的(weakConditionalWeakTable.IsAlive!);
}

私有静态无效的Test2()
{
A A =新的A();
的WeakReference weakB =新的WeakReference(A·B);

B.AllBs.Clear();
A·B = NULL;

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

Debug.Assert的(weakB.IsAlive!); //需要收集第二次被收集
}
}

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

}


解决方案

一个有时的WeakReference 的-irksome的限制是每个的WeakReference 如果,如果没有强烈的层次存在引用到<$可能无效C $ C>的WeakReference 的本身的,如果 trackResurrection 构造函数的参数是真正和即使的的WeakReference 是根深蒂固的目标的。这种现象的原因是,一个的WeakReference 有一个非托管资源(GC手柄)茎,如果终结了的WeakReference 没有清理GC手柄,它永远不会得到清理,并构成了内存泄漏。



如果有必要为对象的终结利用的WeakReference 对象,该对象必须作出一些规定,以确保这些对象仍然坚决引用。我不知道最好的模式是实现这一目标是什么,但 ConditionalWeakTable< TKEY的,TValue> 这是在.NET 4.0中添加的可能是有用的。这就像词典℃的点点; TKEY的,TValue> 除外,只要一表本身强烈引用和给定的关键是大力引用,其相应的价值将被视为为强引用。请注意,如果 ConditionalWeakTable 只要拥有一个条目链接X到Y,和Y表,然后根据X或Y仍然存在,该表将继续担任好。


Hi I have code here where I don't understand why I hit the breakpoint (see comment).

Is this a Microsoft bug of something I don't know or I don't understand properly ?

The code was tested in Debug but I think it should not changes anything.

Note: You can test the code directly in a console app.

JUST FOR INFORMATION... following supercat answer, I fixed my code with proposed solution and it works nicely :-) !!! The bad thing is the usage of a static dict and the performance the goes with it but it works. ... After few minutes, I realized that SuperCat give me all hints to do it better, to workaround the static dictionary and I did it. Code samples are:

  1. Code with the bug
  2. Code corrected but with a static ConditionalWeakTable
  3. Code with ConditioalWeakTable that include the SuperCat tricks (thanks so much to him !)

Samples...

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

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

Version corrected:

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

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

Code with ConditioalWeakTable that include the SuperCat tricks (thanks so much to him !)

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

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

}

Following question of CitizenInsane... I don't remember exactly why I did what I did... I found my sample but wasn't sure about my intention at that time. I tried to figure it out and came with the following code which I thing is more clear but still don't remember my original need. Sorry ???

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

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

}

解决方案

A sometimes-irksome limitation of WeakReference is that a WeakReference may be invalidated if if no strongly-rooted reference exists to the WeakReference itself, and this may occur even if the trackResurrection constructor parameter was true, and even if the target of the WeakReference is strongly rooted. This behavior stems from the fact that a WeakReference has an unmanaged resource (a GC handle) and if the finalizer for the WeakReference didn't clean up the GC handle, it would never get cleaned up and would constitute a memory leak.

If it will be necessary for an object's finalizers to make use of WeakReference objects, the object must make some provision to ensure that those objects remain strongly referenced. I'm not sure what the best pattern is to accomplish this, but the ConditionalWeakTable<TKey,TValue> that was added in .net 4.0 may be useful. It's a little bit like Dictionary<TKey,TValue> except that as long as a the table itself is strongly referenced and a given key is strongly referenced, its corresponding value will be regarded as strongly referenced. Note that if a ConditionalWeakTable holds an entry linking X to Y, and Y to the table, then as long as X or Y remains, the table will remain as well.

这篇关于C#WeakReference的对象是终结NULL虽然依然强烈引用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆