views:

198

answers:

7
class GCTest {
     static Object r1; 

     static void Main() {
         r1 = new Object();
         Object r2 = new Object();
         Object r3 = new Object();
         System.GC.Collect(); // what can be reclaimed here ?

         r1 = null;
         r3.ToString();
         System.GC.Collect(); // what can be reclaimed here ?
     }
    }

// code from - DonBox's Essential .Net

A: 

To me it means the first object in a reference chain. ie A refers to B and B to C etc. A is a Root as nothing else refers to it.

At the first one.

r1 Is static and GC test is holding a reference to it so cannot be collected, while r2 can be collected. r3 will be unless you're in a Debugger or GC.KeepAlive is called

At the second one

r1 can be collected as it no longer in scope r2 is already collected And dependent on whether r3 was collected, it may have raised an exception, or will be collected.

Preet Sangha
Why do you think it would raise an exception ?
DotNetGuy
If it was already collected. Of course I could be wrong
Preet Sangha
+2  A: 

At the first line, there are no further references to r2, so this object could be reclaimed at this point. At the last line, the object created for r1 no longer has a reference, and the local variables r2 and r3 are finished with, so all three objects could be reclaimed at this point. However, this will depend on how it is all compiled.

The three are all root references, as nothing else refers to them. Had r1 been an instance field rather than a static one, then a reference to your GCTest class would have made the r1 reference not a root reference.

David M
Note that if you're running under the debugger, the local references will be artificially extended to last for the whole method, so that you can inspect variables.
Lasse V. Karlsen
@David - Why r2 is reclaimed in 1st call ? Why not r1 ?
DotNetGuy
I thought on the 1st call r2 should not be reclamed because *there is* a reference on *the stack*. What "no further references" means? Is GC really that smart to take into account the rest of code?
Roman Kuzmin
`r2` nay be reclaimed, it depends on how smart the compiler is. Similarly `r1` actually, but this requires a slightly different level of "smartness"
David M
A smart compiler should not create the reference r2. Then, yes, the object from that line should be collected on the 1st call.
Roman Kuzmin
+2  A: 

Try your code with WeakReference and you will see, when these references are alive. Try also to go outside your method. GC only reclaims r1. WeakReference can give you better insight, when your references go dead.

The response with exception is of course a nonsense. The only exception that can r3.ToString() rise is null reference exception, but iff r3 is null.

Karel Frajtak
Hi Karel,Can you please post the code which you are talking about.
DotNetGuy
It can't be formatted in the comment :(static WeakReference wr1; public static void MethodA(){ object r1 = new Object(); wr1 = new WeakReference(r1); Console.Out.WriteLine("r1 = {0}, wr1.Alive {1}", r1, wr1.IsAlive); //wr1 is alive} public static void RunSnippet(){ MethodA(); Console.Out.WriteLine("wr1.Alive {0}", wr1.IsAlive); //wr1 is alive GC.Collect(); Console.Out.WriteLine("After GC: wr1.Alive {0}", wr1.IsAlive); //wr1 is not alive}
Karel Frajtak
A: 
  • object that allocated in Main() and assigned to r1 will be collceted because it's unreachable after r1 set to null.
  • r2 and r3 are local variables in Main() and therefore they are roots in this context.

    fixed, thx

Andrei Taptunov
Your comment for r1 is incorrect I think. There's a difference between a static object and a static method.
Will Eddins
A: 

I see no conclusion to my question. More confusing now !

DotNetGuy
A: 

While I can't give a detailed answer to your question, I can point you to a good paper about garbage collection techniques:

Paul R. Wilson (1994): Uniprocessor garbage collection techniques

If I remember correctly, it contains a definition of root references.

stakx
+1  A: 

The GC may collect whichever object for which it can prove that it makes no difference for the application. One way to think about it is the following: objects are allocated forever but the implementation is allowed to reclaim memory as long as it makes sure that you cannot notice it.

In your example, at the first GC.Collect(), the first object reference has been written to r1, and r1 is a static variable, thus somehow "permanent". From the point of view of the GC, just about any code could use that static variable, so the GC cannot make sure that the first object will not be used. Therefore it cannot collect it. The second object, on the other hand, is written in a local variable. The local variable is limited to the method code (it disappear when the method exits) and the GC is able to notice that r2 will not be used anymore (the GC only needs to look at the method, not the whole code, so this is part of what the GC can reasonably do). Therefore, the GC may collect the second object (there is no obligation here).

The third object will still be used after the first GC.Collect(): its ToString() method is called. Hence the GC should not collect it. However, it is conceivable that the VM notices that this is a true Object instance (not any other class), inlines the call to ToString(), and further concludes that the whole ToString() call is an elaborate way of doing nothing (no visible outcome). As such, the GC might be able to collect the third object and skip the call to ToString() altogether.

Of course the GC is free not to collect things as well. The whole point of the GC is that it runs "behind the scene", without impacting the code execution (this is not entirely true in presence of finalization, but that's an extra layer of complexity which should not be envisioned until you master the basics).

Wilson's article is indeed a must-read for anybody wishing to understand what garbage collection does.

Thomas Pornin