2

I have written a unit test to comfirm the “Dispose” on my class does unhooks all events and disposes a timer that references the object.

However sometimes WeakReference.IsLive() returns true when I would expect it to return false?

So is there a delay after a full GC before WeakReference.IsLive() is updated?

If not, can you think of anything else that would be giving me unrepeatable results?

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

See also Testing Finalizers and IDisposable for a related but different question.

How can I write a unit test to determine whether an object can be garbage collected? has a soltuion that includes calling GC.WaitForPendingFinalizers(), however I rather not call GC.WaitForPendingFinalizers() as I wish to prove that my dispose works and if it worked there will be no need for any finalizers to run.

Community
  • 1
  • 1
Ian Ringrose
  • 51,220
  • 55
  • 213
  • 317

3 Answers3

0

Since the Collect method doesn't block then I'm guessing -- and this is only a guess -- that the GC hasn't collected the object at the point that you test IsAlive.

(And don't forget that you can only trust IsAlive when it returns false.)

I suppose the solution might have to be a blocking call to something like WaitForPendingFinalizers, even if you don't have any finalizers of your own to wait for. (I'm not sure if there are other suitable blocking methods that you could use instead.)

LukeH
  • 263,068
  • 57
  • 365
  • 409
  • I always thought that the Collect method blocked by default, have you got an source that says it does not block? – Ian Ringrose Nov 25 '10 at 13:51
  • 1
    @Ian: I think you might be right. I can't find any definitive documentation to confirm either way. The GC itself doesn't necessarily suspend all app threads for the duration of the entire collection (unless you're running the non-concurrent version of the workstation GC), but I've done a few tests here that suggest that the `Collect` method *is* blocking. Maybe an expert can chime in with some concrete answers... – LukeH Nov 25 '10 at 16:33
0

This was the issue that I had:

When you call Dispose() on a System.Timers.Timer it may return before the Win32 timer has been destroyed. Hence there is still a “root” in unmanaged space keeping the timer alive. The time had an event handler that that kept my object alive.

As this is very timely related, most of the time the Timer would get GCed and so would my object. However sometimes (say 1 in 10 times) the Timer would be kept alive and so would my object.

A short Sleep() would make my test pass 100% of the time, so does unhooking the event on the timer before disposing it, so the timer can’t keep my object alive.

see also How do I safely dispose a System.Timers.Timer?

Community
  • 1
  • 1
Ian Ringrose
  • 51,220
  • 55
  • 213
  • 317
-2

If the IsLive property of a WeakReference returns false, that means the reference is and will forever be kaput, and there's no need to check its value. If it returns true, that means the reference might be alive, but one won't really know until one tries to capture its value into a strong reference. One should not rely upon a WeakReference to be invalidated with any particular degree of timeliness, nor should one take its value unless one is actually interested in it. If one is doing something like cleaning up a list of WeakReferences, removing the ones that have died, the IsAlive property allows one to identify the ones that are well and truly dead without creating strong references to ones that might otherwise be eligible for garbage collection. There's no particular guarantee as to when any of the weak references in the list would be eligible for cleanup, but (1) such eligibility would be more timely in the presence of memory pressure; (2) in the absence of memory pressure, timeliness would not generally be an issue.

supercat
  • 77,689
  • 9
  • 166
  • 211