Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When is an object eligible for garbage collection? [duplicate]

In the book C# 5.0 in a nutshell by Joseph Albahari I found this

enter image description here 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?

like image 663
developer747 Avatar asked Jul 05 '13 23:07

developer747


4 Answers

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.

like image 133
Ian Boyd Avatar answered Sep 28 '22 01:09

Ian Boyd


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.)

like image 35
Guffa Avatar answered Sep 28 '22 03:09

Guffa


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.

like image 34
Reed Copsey Avatar answered Sep 28 '22 03:09

Reed Copsey


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
like image 22
terrybozzio Avatar answered Sep 28 '22 02:09

terrybozzio