12

I have an object, which I believe is held only by a WeakReference. I've traced its reference holders using SOS and SOSEX, and both confirm that this is the case (I'm not an SOS expert, so I could be wrong on this point).

The standard explanation of WeakReferences is that the GC ignores them when doing its sweeps. Nonetheless, my object survives an invocation to GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced).

Is it possible for an object that is only referenced with a WeakReference to survive that collection? Is there an even more thorough collection that I can force? Or, should I re-visit my belief that the only references to the object are weak?

Update and Conclusion

The root cause was that there was a reference on the stack that was locking the object. It is unclear why neither SOS nor SOSEX was showing that reference. User error is always a possibility.

In the course of diagnosing the root cause, I did do several experiments that demonstrated that WeakReferences to 2nd generation objects can stick around a surprisingly long time. However, a WRd 2nd gen object will not survive GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced).

bmargulies
  • 97,814
  • 39
  • 186
  • 310
Kennet Belenky
  • 2,755
  • 18
  • 20
  • 1
    Objects _aren't_ held by WeakReferences. That's precisely their point. There's likely something else holding them alive. – zneak Apr 20 '10 at 02:39
  • What happens when you call GC.GetGeneration(yourWeakReference)? Does it return something or throw an exception? – MusiGenesis Apr 20 '10 at 02:40
  • What does !gchandles and !gcroot show? – Nathan Howell Apr 20 '10 at 02:42
  • @zneak Yes, objects aren't _held_ by weak references. However, that does not rule out the possibility that the GC takes WRs as a hint to keep stuff around. It would not violate the GC contract if it had logic in there that said, "If I've got sufficient memory, sure... keep the WRs alive." – Kennet Belenky Apr 20 '10 at 03:18
  • @Nathan Howell I don't have access to the machine with the code on it right now, but I can tell you from memory that !refs in SOSEX says the only reference holder is an object of type >. !gcroot traces back to a "WeakSh" – Kennet Belenky Apr 20 '10 at 03:19
  • If the only root is a WeakSh handle it should be collected next time around. Try grabbing psscor2 and using the !gcwhere command to see what gc generation it's currently in. – Nathan Howell Apr 20 '10 at 15:26
  • 2
    So the question contains its own answer. I guess that makes it 'too localized'. @Kennet Belenky, how about adding an answer containing your conclusion? – bmargulies Jun 13 '10 at 00:55

5 Answers5

0

I recommend you to check for the "other" references to the weakly referenced objects. Because, if there is another reference still alive, the objects won't be GCed.

flatline
  • 11
  • 2
0

As per wikipedia "An object referenced only by weak references is considered unreachable (or "weakly reachable") and so may be collected at any time. Weak references are used to avoid keeping memory referenced by unneeded objects"

I am not sure if your case is about weak references...

Srikar Doddi
  • 15,499
  • 15
  • 65
  • 106
  • 1
    I'm well aware of the standard description of weak references. My case is about reexamining the standard description of weak references because I am currently confronted with evidence that suggests that the actual behavior is more sophisticated. – Kennet Belenky Apr 20 '10 at 03:15
0

Try calling GC.WaitForPendingFinalizers() right after GC.Collect().

Another possible option: don't ever use a WeakReference for any purpose. In the wild, I've only ever seen them used as a mechanism for lowering an application's memory footprint (i.e. a form of caching). As the mighty MSDN says:

Avoid using weak references as an automatic solution to memory management problems. Instead, develop an effective caching policy for handling your application's objects.

MusiGenesis
  • 74,184
  • 40
  • 190
  • 334
  • I do have the WaitForPendingFinalizers in my code. I generally agree with you that 99% of the time if you're using WRs, you're doing it wrong. I think this is the 1% case. I have to keep a central list of objects which does not affect the object lifetime. – Kennet Belenky Apr 20 '10 at 03:13
  • Most of the time, when `Foo` holds a reference to `Bar`, it will do so because it "needs" `Bar`. On some occasions, however, `Foo` may hold a reference to `Bar` purely so it can do things *for the benefit of other objects that need `Bar`*. If there aren't any other objects that need bar, `Foo` would just as soon have `Bar`, and the reference to it, replaced with an indication that it no longer needs to serve it. – supercat Oct 05 '12 at 18:57
  • @supercat in your latter case, the thing to use in .NET 4+ is not WeakReference, but [ConditionalWeakTable](http://msdn.microsoft.com/en-us/library/dd287757.aspx). – Anton Tykhyy Oct 05 '13 at 20:46
  • @AntonTykhyy: The `ConditionalWeakTable` class is great, but `WeakReference` fills a different need. I don't think there's any way a CWT can be used to access something which is kept alive by the existence of other references, without either keeping that thing alive itself or using a `WeakReference` to avoid doing so. – supercat Oct 07 '13 at 15:39
  • It depends on what kind of services does Foo provide. Suppose the service is providing a complex value calculated from a Bar. CWT is ideal for caching such values. – Anton Tykhyy Oct 08 '13 at 15:16
0

Weakly referenced objects do get removed by garbage collection. I've had the pleasure of debugging event systems where events were not getting fired... It turned out to be because the subscriber was only weakly referenced and so after some eventual random delay the GC would eventually collect it. At which point the UI stopped updating. :)

Tim Lovell-Smith
  • 15,310
  • 14
  • 76
  • 93
0

Yes it is possible. If the WeakReference is located in another generation than the one being collected, for example, if it is in the 2nd Generation, and the GC only does a Gen 0 collection; it will survive. It should not survive a full 2nd Gen collection that completes and where all finalizers run, however.

codekaizen
  • 26,990
  • 7
  • 84
  • 140