Eri*_*let 4 c# garbage-collection weak-references finalizer
嗨,我在这里有代码,我不明白为什么我会遇到断点(请参阅评论)。
这是我不知道或我不正确理解的 Microsoft 错误吗?
代码在 Debug 中进行了测试,但我认为它不应该改变任何东西。
注意:您可以直接在控制台应用程序中测试代码。
仅供参考...在 supercat 回答之后,我使用建议的解决方案修复了我的代码,并且效果很好:-) !!! 不好的是静态字典的使用和性能随之而来但它的工作原理。...几分钟后,我意识到 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();
}
}
// **********************************************************************
}
Run Code Online (Sandbox Code Playgroud)
版本更正:
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);
}
}
// **********************************************************************
}
Run Code Online (Sandbox Code Playgroud)
使用包含 SuperCat 技巧的 ConditialWeakTable 编写代码(非常感谢他!)
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);
}
}
// **********************************************************************
}
Run Code Online (Sandbox Code Playgroud)
跟随 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
}
}
// **********************************************************************
}
Run Code Online (Sandbox Code Playgroud)
有时令人讨厌的限制WeakReference是,WeakReference如果不存在对WeakReference 自身的强根引用,则 a可能会失效,即使trackResurrection构造函数参数是true,并且即使 的目标WeakReference是强根,也可能发生这种情况。这种行为源于这样一个事实,即 aWeakReference具有非托管资源(GC 句柄),并且如果 的终结器WeakReference没有清理 GC 句柄,它将永远不会被清理,并且会构成内存泄漏。
如果对象的终结器需要使用WeakReference对象,则该对象必须做出一些规定以确保这些对象保持强引用。我不确定实现此目的的最佳模式是什么,但ConditionalWeakTable<TKey,TValue>在 .net 4.0 中添加的模式可能很有用。这有点像,Dictionary<TKey,TValue>只是只要一个表本身被强引用,并且给定的键被强引用,它的对应值就会被视为强引用。请注意,如果 aConditionalWeakTable包含一个将 X 链接到 Y,将 Y 链接到表的条目,那么只要 X 或 Y 保留,表格也将保留。
有2个垃圾收集方面,你不指望:
WeakReference.IsAlive 变为 false 的确切时间。您的代码隐含地假设在终结器运行时会发生这种情况。情况并非如此,当对象被垃圾收集时会发生这种情况。之后对象被放置在终结器队列中,因为它有一个终结器并且 GC.SuppressFinalize() 没有被调用,等待终结器线程完成它的工作。所以有一段时间 IsAlive 是假的,但 ~B() 还没有运行。
对象最终确定的顺序是不可预测的。你隐含地假设 B 在 A 之前完成。你不能做出这个假设。
There's also a bug in the B.Dispose() method, it won't correctly count B instances when the client code explicitly disposed the object. You haven't hit that bug yet.
There is no reasonable way to fix this code. Moreover, it tests something that is already backed by hard guarantees provided by the CLR. Just remove it.
| 归档时间: |
|
| 查看次数: |
2059 次 |
| 最近记录: |