在WeakReference.IsLive()变为false之前,在完全垃圾收集之后是否有延迟?

时间:2010-11-25 09:32:26

标签: .net unit-testing garbage-collection dispose

我已经编写了一个单元测试来确认我的课上的“Dispose”取消了所有事件,并配置了一个引用该对象的计时器。

然而有时 WeakReference.IsLive()在我希望它返回false时返回true?

在WeakReference.IsLive()更新之前,完整的GC之后会有延迟吗?

如果没有,你能想到任何会给我不可重复的结果吗?

WeakReference weekJobWatchDog = new WeakReference(jobWatchDog);
jobWatchDog = null;

// not collected before Dispose called due to timer and events etc
GC.Collect(); GC.Collect();
Assert.IsTrue(weekJobWatchDog.IsAlive);

((IDisposable)weekJobWatchDog.Target).Dispose();

// is now collected as Dispose unlocked all events and dispoed the timer
GC.Collect(); GC.Collect();
Assert.IsFalse(weekJobWatchDog.IsAlive); // sometimes this fails, about 1 in 4 runs

另请参阅Testing Finalizers and IDisposable了解相关但不同的问题。

How can I write a unit test to determine whether an object can be garbage collected?有一个解决方案,包括调用GC.WaitForPendingFinalizers(),但是我不想调用GC.WaitForPendingFinalizers(),因为我希望证明我的处理是有效的,如果它有效,那么就不需要任何终结者要跑。

3 个答案:

答案 0 :(得分:0)

由于Collect方法没有阻止,所以我猜测 - 这只是猜测 - GC在您测试IsAlive时没有收集对象

(不要忘记you can only trust IsAlive when it returns false。)

我认为解决方案可能必须是WaitForPendingFinalizers之类的阻止调用,即使您没有自己的终结器等待。 (我不确定是否有其他合适的阻止方法可供您使用。)

答案 1 :(得分:0)

这是的问题:

当您在System.Timers.Timer上调用Dispose()时,可能在Win32计时器被销毁之前返回。因此,在非托管空间中仍然存在“根”,使计时器保持活动状态。时间有一个事件处理程序,使我的对象保持活着。

由于这是非常及时的相关,大多数时候Timer会得到GCed,我的对象也是如此。然而,有时候(比如10次中的1次),Timer会保持活着,我的目标也是如此。

一个简短的Sleep()会使我的测试在100%的时间内通过,所以在处理它之前解除了计时器上的事件,所以计时器不能让我的对象保持活着。

另见How do I safely dispose a System.Timers.Timer?

答案 2 :(得分:-2)

如果WeakReference的IsLive属性返回false,则表示引用将永远是kaput,并且不需要检查其值。如果它返回true,这意味着引用可能是活着的,但是在尝试将其值捕获到强引用之前,人们才会真正知道。一个人不应该依赖WeakReference在任何特定程度的及时性上失效,除非一个人真正感兴趣,否则他们也不应该接受它的价值。如果正在做一些事情,比如清理WeakReferences列表,删除已经死亡的那些,那么IsAlive属性允许用户识别那些已经完全死亡的属性,而不会创建对可能有资格进行垃圾收集的强引用。没有特别保证列表中的任何弱引用何时有资格进行清理,但(1)在存在内存压力的情况下,这种资格会更及时; (2)在没有记忆压力的情况下,及时性通常不会成为问题。