Possible Duplicate:
Resurrection difference in using Object Initializer
I am having a hard time trying to understand how garbage collector works in C# (I’m using 2012, so c# 4.5). Here is my example code:
public class A
{
public int c;
public A(){}
public A(int pC)
{
c = pC;
}
}
public static void Main()
{
// Test 1
var a = new A {c=199};
var aRef = new WeakReference(a);
a = null;
Console.WriteLine(aRef.IsAlive);
GC.Collect();
Console.WriteLine(aRef.IsAlive);
// Console.WriteLine(GC.GetGeneration(aRef.Target)); //output 1
// Test 2
a = new A (200);
aRef = new WeakReference(a);
a = null;
Console.WriteLine(aRef.IsAlive);
GC.Collect();
Console.WriteLine(aRef.IsAlive);
}
Output is True / True / True / False
It seems to me in both tests, the object on the heap has no root before calling GC.Collect. But it happens that in Test 1, the object get through the force gc run, while in Test 2 it doesn’t. So, is there something mysterious going on about using initializer?
My guess is that there might be “some extra code” when use initializer that would become a strong root for the same object…..
Thanks.
Clearly you are running either the Debug build or have a debugger attached. The garbage collector gets lifetime hints from the just-in-time compiler, it generates a table that indicates in what sections of code a local variable can be referenced. The garbage collector walks the stack of the executing method that was interrupted by the GC and checks the execution location against this table. And counts the reference as valid when it finds a match.
If the code was built in the Debug configuration or when a debugger is attached, the jitter modifies this table and lets the variable stay alive until the end of the method body. This makes it a lot easier to debug the code, you can put the local variable in a watch expression and it will produce a result even when you step past the point where the variable is no longer used.
The answer posted by @Imposter is correct, the hidden temporary variable keeps the first instance of A alive. And the garbage collector considers it valid until the end of the method because you are using the debugger. Your second
a = null;assignment allows the second instance to be garbage collected.What will really happen when you run this code in production is very different. For one, the jitter optimizer will remove the a = null assignments. It knows that those assignments have no useful side-effects so it generates no code for them. Pretty unintuitive, the best way to see this is by taking these steps:
The last option change allows you to keep using the debugger without it affecting the way the jitter generates code. Now the temporary variable will no longer keep the first instance of A referenced, the table generated by the jitter will only mark it as storing a valid reference for the first statement in the method. Run your program and you’ll see:
With the important new insight that setting a reference to null is not actually necessary, the garbage collector is smart enough to not require you to help.