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 prope
A sometimes-irksome limitation of WeakReference
is that a WeakReference
may be invalidated 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 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.
There are two aspects of garbage collection that you didn't count on:
The exact time at which the WeakReference.IsAlive becomes false. Your code implicitly assumes that will happen when the finalizer runs. This is not the case, it happens when the object gets garbage collected. After which the object is placed on the finalizer queue, because it has a finalizer and GC.SuppressFinalize() wasn't called, waiting for the finalizer thread to do its job. So there's a period of time where IsAlive is false but ~B() hasn't run yet.
The order in which objects get finalized is not predictable. You implicitly assume that B is finalized before A. You cannot make this assumption.
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.
The WeakReference _weakB
is available for garbage collection at the same time as the object a
is. You don't have a guarantee of order here, so it could very well be that _weakB
is finalized before object a
.
Accessing _weakB
in the finalizer of A is dangerous, since you don't know the state of _weakB
. I'm guessing that in your case it has been finalized, and that that is causing it to return null for .Target
.