ConditionalWeakTable-GC.Collect()行为,为什么它没有按预期操作



为什么我的武器参考目标在第二枪时还活着?

可能是个虫子吗?如果没有,错误在哪里?

结果:

weakRef.Target is alive = True, expected true because inst keep a hold on SomeClass.
weakRef.Target is alive = True, expected false, because there is no more ref on SomeClass.

代码:

public static class DelegateKeeper
{
private static ConditionalWeakTable<object, Action> cwtAction = new ConditionalWeakTable<object, Action>();
public static void KeepAlive(Action action) => cwtAction.Add(action.Target, action);
}
public class SomeClass
{
public void DoSomething() { }
}
public static class GcHelper
{
public static void Collect()
{
// OK surely overkill but just to make sure. I will reduce it when everyting will be understood.
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true);
GC.WaitForPendingFinalizers();
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true);
GC.WaitForPendingFinalizers();
}
}
SomeClass instanceSomeClass;
WeakReference<Action> weakRef;
[TestMethod]
public void TestLifeOfObject()
{
Init();
GcHelper.Collect();
Debug.WriteLine($"weakRef.Target is alive = {weakRef.TryGetTarget(out _)}, expected true because inst keep a hold on SomeClass.");
RemoveLastReferenceOnSomeClass();
GcHelper.Collect();
Debug.WriteLine($"weakRef.Target is alive = {weakRef.TryGetTarget(out _)}, expected false, because there is no more ref on SomeClass.");
}
private void Init()
{
instanceSomeClass = new SomeClass();
var action = instanceSomeClass.DoSomething;
weakRef = new WeakReference<Action>(action);
DelegateKeeper.KeepAlive(action);
}
private void RemoveLastReferenceOnSomeClass()
{
instanceSomeClass = null;
}

文档对此不清楚,但ConditionalWeakTable似乎对所添加的value有强引用,即使key只是弱引用。

通过查看源代码,它使用了DependentHandle,文档中写道:

如果没有其他强引用,以给定对象实例为目标的DependentHandle值不会使目标保持活动状态,但只要目标处于活动状态,它就会对依赖对象实例保持活动状态。

因此,因为您添加了action作为值,而actionlambda本身捕获了inst,因此您强烈引用了inst

不过,你可以存储其他对象,你不在乎是否有字符串引用。看看这个fiddle,new object()被存储为值,它可以正常工作。我不知道你的用例,所以我不知道这是否适用于你。

但我也会注意到微软的状态:

避免使用弱引用作为内存管理问题的自动解决方案。相反,开发一个有效的缓存策略来处理应用程序的对象。

我在微软也问过这个问题,从Viorel-1得到了正确的答案:learn.Microsoft.com

答案:删除第一个";Debug.WriteLine;样品中。

原因与这个问题完全相同:使用WeakReference[重复]时对象生命的意外行为,而这个问题:收集仍在作用域中的对象-GC.Collect

原因是:如果我使用weakReference out参数丢弃**:"_&";,有一个对所使用的invisble返回值的引用,该值在作用域中。在该引用停留在作用域(函数的时间(之前,存在对weakReference目标的引用。我的意思是weakReference停留在状态";可到达/有根";直到函数结束。

通过去除第一";Debug.WriteLine";,我得到了预期的结果。Microsoft参考

**";丢弃";是应用程序代码中有意未使用的占位符变量。在代码中;丢弃";表示为"_"(下划线(。

最新更新