In the book C# 5.0 in a nutshell by Joseph Albahari I found this
where it says as soon as you pass the line of code where a variable is last used, the object referenced by it is eligible for garbage collection (That is if no other variable holds a reference to that object).
However according to this lecture from UC Berkley, as long as a reference to the object exists on the stack, it won't be garbage collected. My understanding is, until the method returns, the variable stays on the stack. Which means any object referenced by it is alive until the method returns.
Is this an error in the book or does java and .net garbage collection work differently?
However according to this lecture from UC Berkley, as long as a reference to the object exists on the stack, it won't be garbage collected.
You're right. What you're missing is that a reference no longer exists on the stack.
For the code that constructs an object on the stack:
StringBuilder ref1 = new StringBuilder("object1");
the variable ref1
is stored on the stack, in some memory location:
0x403730:
Stack Pointer -> 0x40372C: pointer to ref1
0x403728: saved value of EBP
0x403724: saved value of return address
0x403720
Now comes the next line:
StringBuilder ref2 = new StringBuilder("object2");
Where is the pointer to ref2
going to be stored? On the stack: yes. But where on the stack? In the same memory location that was being used for ref1
of course!:
0x403730:
Stack Pointer -> 0x40372C: pointer to ref2
0x403728: saved value of EBP
0x403724: saved value of return address
0x403720
It would be silly to simply push another value onto the stack:
Stack Pointer -> 0x403730: pointer to ref2
0x40372C: pointer to ref1
0x403728: saved value of EBP
0x403724: saved value of return address
0x403720
It would be silly because ref1
isn't needed anymore.
That's why ref1
is eligible for garbage collection: there are no longer any references to it.
The book is correct.
In .NET, the garbage collector has information of where in the code the variable is used, and as soon as it's unused, the object is eligible for garbage collection.
(However, if you run the code with a debugger attached, the garbage collector changes behaviour. It keeps the object for the entire scope of the variable, not only where the variable is used, so that the object can be investigated in the debugger.)
My understanding is, until the method returns, the variable stays on the stack. Which means any object referenced by it is alive until the method returns.
The JIT is free to remove the object reference ("variable") any time after it's last usage, so this is not necessarily true.
as long as a reference to the object exists on the stack, it won't be garbage collected
This is true - but the JIT may change when this variable no longer "exists on the stack" in a way that doesn't necessarily match your code.
In C# 5, this can get really confusing, as well, as async
methods can get rewritten in ways that variables stick around longer than you'd expect in some scenarios.
That being said, if you need to guarantee that an object is eligible at some point, setting the variable(s) referencing the object to null
explicitly allows you to control when it becomes eligible explicitly.
this is the CIL code generated from that method:
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 46 (0x2e)
.maxstack 2
.locals init ([0] class [mscorlib]System.Text.StringBuilder ref1,
[1] class [mscorlib]System.Text.StringBuilder ref2,
[2] class [mscorlib]System.Text.StringBuilder ref3)
IL_0000: nop
IL_0001: ldstr "object1"
IL_0006: newobj instance void [mscorlib]System.Text.StringBuilder::.ctor(string)
IL_000b: stloc.0 //here ref1 is stored on stack.
IL_000c: ldloc.0 //here it is loaded to be displayed by the console.writeline
IL_000d: call void [mscorlib]System.Console::WriteLine(object)
IL_0012: nop //from here on no more reference to ref1.
IL_0013: ldstr "object2"
IL_0018: newobj instance void [mscorlib]System.Text.StringBuilder::.ctor(string)
IL_001d: stloc.1
IL_001e: ldloc.1
IL_001f: stloc.2
IL_0020: ldloc.2
IL_0021: call void [mscorlib]System.Console::WriteLine(object)
IL_0026: nop //and from here on ref2 and ref3 also.
IL_0027: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
IL_002c: pop
IL_002d: ret
} // end of method Program::Main
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With