Saturday 4 November 2017

c#: What happens in a variable in memory?

If you have this
code:


void Main()
{

string name;
}

Then,
when compiled (in LINQPad) with compiler optimisation you get the following
IL:



IL_0000:
ret

And without
optimisation:



IL_0000: nop
IL_0001:
ret

There is no memory allocated for this
declaration - just a NOP operation as a placeholder in the IL for the unoptimised
code.


When your program is
this:


void Main()
{

string name =
"Jack";
}

Then you
compiler optimised code is:



IL_0000:
ret

The compiler simply ignores the unused
variable.


The unoptimised code generates
this:



IL_0000: nop
IL_0001: ldstr
"Jack"
IL_0006: stloc.0 // name
IL_0007:
ret

Obviously the unoptimised code is more
explanatory, so I'll only show unoptimsed code from now on unless I explicitly say
otherwise.


Now let's make the code do something more
interesting.


void
Main()
{
string name = "Jack";

Console.WriteLine(name);
}

This
produces:



IL_0000: nop
IL_0001:
ldstr "Jack"
IL_0006: stloc.0 // name
IL_0007: ldloc.0 //
name
IL_0008: call System.Console.WriteLine
IL_000D:
nop
IL_000E: ret

What's interesting
is if we change this code to this:


void
Main()
{
int answer = 42;

Console.WriteLine(answer);
}

We
get this:



IL_0000: nop
IL_0001:
ldc.i4.s 2A
IL_0003: stloc.0 // answer
IL_0004: ldloc.0 //
answer
IL_0005: call System.Console.WriteLine
IL_000A:
nop
IL_000B: ret

The code is
virtually the same as the string
example.


The ldstr call is getting
a reference to a string literal (which is store in the String Pool on the Large Object
Heap (not the normal heap which is the Small Object Heap) and pushing it on the
evaluation stack.


The ldc.i4.s is
pushing a reference to the number 42 on to the evaluation
stack.


Then, in both cases,
stloc.0 is storing the value on top of the evaluation stack
into the zeroth local memory location for the method.


Then,
in both cases again, ldloc.0 is loading the value from the
zeroth local memory location and putting it on the evaluation
stack.


You can probably imagine what the compiler might do
if it were optimising this code.


Finally the
System.Console.WriteLine is
made.


Now let's look at that pesky
string code in more detail.


I said
that it's stored in the Intern Pool. Let's check that.


Take
this code:


void Main()
{

string name = "Jack";

Console.WriteLine(String.IsInterned(name));
}

It
produces:



IL_0000: nop
IL_0001:
ldstr "Jack"
IL_0006: stloc.0 // name
IL_0007: ldloc.0 //
name
IL_0008: call System.String.IsInterned
IL_000D: call
System.Console.WriteLine
IL_0012: nop
IL_0013:
ret

And it outputs
Jack to the console. It can only do that if
System.String.IsInterned returns an interned
string.


Take this program to show the
opposite:


void Main()
{

string name = String.Join("", new [] { "Ja", "ck" });

Console.WriteLine(String.IsInterned(name));
}

It
pushed out null to the console - meaning that the string
name isn't interned so in this case
name is stored on the heap (Small Object
Heap).


Let's look at your second bit of
code:


void Main()
{
for
(int i = 0; i < 20; i++)
{
Run();

}
}
private void Run()
{
int age =
20;
}

If we look at
optimised IL then the Run method looks like
this:



Run:
IL_0000:
ret

The unoptimised IL is
this:



Run:
IL_0000:
nop
IL_0001: ldc.i4.s 14
IL_0003: stloc.0 // age
IL_0004:
ret

And, like my earlier example with an
int, it is loading the literal value
20 (or 14 in hex) onto the evaluation
stack and then immediately storing it in the local memory for the method, and then
returns. And hence it is repeating using the same memory 20 times for the local variable
age.

No comments:

Post a Comment

php - file_get_contents shows unexpected output while reading a file

I want to output an inline jpg image as a base64 encoded string, however when I do this : $contents = file_get_contents($filename); print &q...