Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Variable lifetime

What happends to variable when line of execution goes outside of code block? For example:

1  public void myMethod()
2  {
3     int number;
4     number = 5;
5  }

so, we declare and set variable. When it goes outside of code block (line 5) what happends to variable number?

Here is another example with creating instance of class:

7   public void myMethod()
8   {
9      Customer myClient;
10     myClient = new Customer();
11  }

When it goes outside of code block (line 11) what happends to object reference myClient?

I guess in both cases variable is allocated but when it is deallocated?

like image 860
ExpertLoser Avatar asked Dec 25 '22 17:12

ExpertLoser


1 Answers

As a variable it's a concept in the C# language. Nothing "happens" to it outside of the code block, because it is inside the code block. Nothing happens to the word word outside this sentence.

Of course, you mean what happens to the thing that variable becomes when the code is run, but it's worth remembering the distinction, because in considering that question we're shifting to levels where variables are not as they are in C#.

In both cases the code is turned into CIL, and then into machine code when it is run.

The CIL could differ quite a bit. For example, here's how the first looks when compiled in debug mode:

.method public hidebysig instance void myMethod () cil managed 
{
  .locals init ([0] int32) // Set-up space for a 32-bit value to be stored
  nop                      // Do nothing
  ldc.i4.5                 // Push the number 5 onto the stack
  stloc.0                  // Store the number 5 in the first slot of locals
  ret                      // Return
}

And here's how it looks when compiled to release:

.method public hidebysig instance void myMethod () cil managed 
{
  ret                      // Return
}

Since the value isn't used, the compiler removes that as useless cruft and just compiles a method that immediately returns.

If the compiler didn't remove such code, we might expect something like:

.method public hidebysig instance void myMethod () cil managed 
{
  ldc.i4.5                 // Push the number 5 onto the stack
  pop                      // Remove value from stack
  ret                      // Return
}

Debug builds store things for longer, because examining them is useful for debugging.

When release builds do store things in the array of locals, they are also more likely to reuse slots within the method.

This is then turned into machine code. It would be analogous in how it would work in that it would either produce the number 5, store it locally (on the stack or in a register) and then get rid of it again, or alternatively do nothing because the unused variable has been removed. (Perhaps not even executing the method; the method could be inlined, and then since it doesn't do anything be effectively removed entirely).

With a type with a constructor, there's slightly more going on:

.method public hidebysig instance void myMethod () cil managed 
{
  .locals init ([0] class Temp.Program/Customer)       // Set-up space for a reference to a Customer

  nop                                                  // Do nothing.
  newobj instance void SomeNamespace/Customer::.ctor() // Call Customer constructor (results in Customer on the stack)
  stloc.0                                              // Store the customer in the frist slot in locals
  ret                                                  // Return
}

.method public hidebysig instance void myMethod () cil managed 
{
  newobj instance void SomeNamespace/Customer::.ctor() // Call Customer constructor (results in Customer on the stack)
  pop                                                  // Remove value from stack
  ret                                                  // Return
}

Here both call the constructor, and even the release build does that, because it has to ensure that any side-effects still happen.

There is also more happening if Customer is a reference type. If it's a value-type then all of it is held in the stack (though it may have fields that are reference types in turn). If it's a reference type then what is held in the stack is a reference to an object in the heap. When there are no longer any such references on the stack the garbage collector won't find it in its sweep to find which objects it can't collect, and it can be collected.

In the release version, there might never be a memory location or register that holds that reference once the constructor returns. Indeed, there might not be one even when the constructor was running (if no field accesses or other implicit or explicit use of this happen) or it might have been wiped part-way through that (once such accesses had finished), so garbage collection could happen before the constructor has even finished.

More likely it will hang around in heap memory for some time after the method has returned, because the GC hasn't run yet.

like image 114
Jon Hanna Avatar answered Dec 31 '22 14:12

Jon Hanna