11

Q. Is there a way to find out if an object has any "strong references" to it?


Raymond Chen hinted that a solution might be possible:

You want to know whether the reference count is zero or nonzero. For that, use WeakReference.

Notes

  • i have a "weak reference" to the object (using a WeakReference). If i had a strong reference the answer would immediatly be: "Yes. You have a strong reference to the object."
  • the garbage collector exposes no answers
  • the IsAlive property can only tell you if an object has been collected, or not. Not if there are strong references to it, or not. (An object with no references can be uncollected - the GC just hasn't gotten around to it yet)
  • objects in .NET are not reference counted
  • not all objects have to implmenet the IDisposable interface
  • not all objects are mine

Code Sample

This code sample demonstrates the problems with relying on forcing a garbage collection and the WeakReference's IsAlive property to determine if an object has any outstanding references to it.

WeakReference m_wr = null;

...

for (int i = 0; i < 1000000; i++)
{
   Pig p = new Pig();
   m_wr = new WeakReference(p);
}

...

GC.Collect();
if (m_wr.IsAlive)
   Environment.FailFast("All objects should have been collected by now");
Ian Boyd
  • 246,734
  • 253
  • 869
  • 1,219
  • Why do you need to know this? – Tim Robinson Aug 12 '10 at 17:18
  • "For that, use WeakReference" - Raymond is alluding to `WeakReference.IsAlive` – Tim Robinson Aug 12 '10 at 17:19
  • @Tim Robinson If `IsAlive` returns `true` all i can infer is that the object has **not** been collected. If `IsAlive` returns `false` all i can infer is that the object **has** been collected. i want to know if an object has any "strong references" to it. An object with strong references will not be collected, but not all uncollected objects have strong references to them. – Ian Boyd Aug 12 '10 at 17:24
  • 8
    Why i need to know this? i could make up a few reasons `1.` For the betterment of all man-kind to expand the horizon of human knowledge `2.` i'm performing a thesis on the physical degredation effects of strongly-referenced objects on DDR RAM in underclocked systems. `3.` i want to know if it's safe to call Dispose on a database connection. `4.` i want to catch a bug where someone's referencing an object, but i'm sure nobody should be. `5.` Because someone else asked but people refused to answer the question. – Ian Boyd Aug 12 '10 at 17:27
  • @Mark's advice to call `GC.Collect; WR.IsAlive` is good. The only sure way to answer "where are all the strong references?" is to perform a garbage collection. The alternative might be to suspend the program, track down GC roots and traverse object references yourself, but that's what the GC is doing anyway. – Tim Robinson Aug 12 '10 at 17:40

3 Answers3

11

Nope, not without using the debugger API.

As you say, objects aren't reference counted... so the only way of finding out would be to crawl the heap, which normally just happens as part of garbage collection.

Note that even after there are no "normal" strong references, the object could be resurrected as part of finalization anyway - effectively the finalizer queue has a reference to it, if it has a finalizer. Maybe you wouldn't want to include the object as "reference-less" in that situation anyway.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • "No" was the answer i assumed. We'll see if anyone disagrees. But with you 205k of rep, i think you're going to be the authority on this. – Ian Boyd Aug 12 '10 at 17:28
  • You could try adding an implicit conversion to object as weakreference takes it as parameter and add a counter in that implicit conversion method but all other things that get it as object would also increment it. Also implicit conversion to object could be stuck infinitely repeating itself. Gold medals per point is important too. – huseyin tugrul buyukisik Mar 25 '17 at 21:54
  • @huseyintugrulbuyukisik: You can't add an implicit conversion to `object`, and to be honest I don't see how that would help. Nor do I understand the last part of your comment... – Jon Skeet Mar 26 '17 at 06:57
  • @JonSkeet is it because of garbage collection? Last part I mean more frequent gold medals looks cool and you sustained frequency for 530 medals. – huseyin tugrul buyukisik Mar 26 '17 at 08:58
  • @huseyintugrulbuyukisik: Is what because of garbage collection? The prohibition of implicit conversions to object? No, not at all. – Jon Skeet Mar 26 '17 at 09:04
4

You should read Raymond Chen's post about Reference counts from yesterday. After that - you should decide if that's something you really need to do and why. Then come back and tell us why.

Hmm, it seems you've read the post - absorbed minor details and missed the point.


i want to know if it's safe to call Dispose on a database connection.

Read the docs. Dispose calls Close. Close is safe to call as much as you like.

An application can call Close more than one time. No exception is generated.

Amy B
  • 108,202
  • 21
  • 135
  • 185
  • All implementations of `IDisposable.Dispose` should behave nicely if called more than once. Furthermore, finalizers should also behave the same way, because already-finalized objects can be resurrected and re-finalized. – Tim Robinson Aug 12 '10 at 17:41
  • i added a comment to original question stating why. You will find my name in the comments of that blog post, asking the same question - that's when i decided to ask here. Also, calling `Dispose` or `Close` twice on a database connection is not the worst that can happen. – Ian Boyd Aug 12 '10 at 17:41
  • Also, **important point**, i wasn't asking about reference counts. – Ian Boyd Aug 12 '10 at 17:49
3

First call GC.Collect() and then check WeakReference.IsAlive. If it is true (i.e., it hasn't been collected after calling GC.Collect) then there is a strong reference somewhere.

Mark Cidade
  • 98,437
  • 31
  • 224
  • 236
  • 3
    +1 for correctness. However, if you call GC.Collect to check object reference counts, you will push objects into later generations - making them live longer if you ever stop manually invoking the GC. As such - this is a bad practice. – Amy B Aug 12 '10 at 17:26
  • Not technically correct. That forcing a generation-zero collection does not guarantee that the object in question will be collected: It may have been promoted into a higher generation – Ian Boyd Aug 12 '10 at 17:36
  • It'll only be promoted to a higher generation if there are still outstanding references. If the GC determines there are no references, it'll be left alone or collected. – Tim Robinson Aug 12 '10 at 17:42
  • @Tim Robinson It's not strictly true. There is a heuristic involved in promoting objects to higher generations. It's not something you can count on now. And it's certainly not documented to not change tomorrow. Also, objects *never* being collected is a valid scenario (i.e. the `null` garbage collector is a valid garbage collector). Nothing says that the garbage collector has to do anything when you call `Collect` - that's an internal implementation detail. – Ian Boyd Aug 12 '10 at 17:45
  • @Ian: MSDN says, that GC.Collect "forces an immediate garbage collection of all generations." http://msdn.microsoft.com/en-us/library/xe0c2357.aspx – Mark Cidade Aug 12 '10 at 18:05
  • @Mark Cidade The GC is allowed to do nothing. Also because it's threaded, it's a race condition. – Ian Boyd Aug 12 '10 at 19:14
  • Where does it say that the .NET GC may do nothing when calling GC.Collect()? Chen's null GC argument is only theoretical. And I don't see how it's a race condition since GC.Collect() doesn't return until it's completed the collection. – Mark Cidade Aug 12 '10 at 19:52
  • 1
    @Mark Cidade It doesn't say that the GC may do nothing when calling `GC.Collect()`; nor does say what the GC will do. It says "Forces an immediate garbage collection of all generations."). An optimization that is allowed to exist where it does nothing, "Okay, i've performed the collection operations - and i collected nothing since there's no memory pressure." – Ian Boyd Aug 13 '10 at 14:51
  • Note checking `IsAlive` for `true` is a race conditon. See remarks in documentation: "Because an object could potentially be reclaimed for garbage collection immediately after the IsAlive property returns true, using this property is not recommended unless you are testing only for a false return value." – Jack Ukleja Apr 21 '17 at 04:13
  • We established almost 7 years ago that this isn't a reliable method. – Mark Cidade Apr 21 '17 at 04:40