2

If I have this variable:

string name;

will it be allocated a location in the memory? Or it will only get allocated memory when I initialize it to a specific value? I.e.,

string name = "Jack";

For example, consider the following code:

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

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

What will happen to the age value in the memory? Will it be removed from memory in each execution of the Run method? Or will it stay in the memory after the code is executed and removed after the program that uses it close?

  • 2
    related question [Memory Allocation stack vs. heap](https://stackoverflow.com/questions/4487289/memory-allocation-stack-vs-heap) – hatchet - done with SOverflow Oct 26 '17 at 23:53
  • 2
    Side note: A really aggressive optimizer might notice your code has no visible effects and remove everything. ....I really need to write that extension/analyzer for VS/Roslyn, I miss it from Eclipse - there's nothing quite like watching half the stuff you've written disappear, keeps you very honest about what you're using. – Clockwork-Muse Oct 27 '17 at 00:01
  • Ah yes, good ol' C++ optimizing compilers.. you've got classes, function calls and loops in the code? naah, you must have imagined that – quetzalcoatl Oct 27 '17 at 00:06
  • Why did you select Babak's answer as the correct one? Almost everything he said was wrong, partly wrong, or confusing. – Enigmativity Oct 30 '17 at 03:22

4 Answers4

1

string name;

If this is your only statement, the compiler will probably optimize and remove it. Without optimizations, this will be a reference to null.

string name = "Jack";

This will create a memory allocation in the heap to store the string itself. It will also generate a pointer in your stack with the address of the allocated heap memory. Upon exiting the method and stack popped, the allocated memory in the heap will no longer have a reference and can be flagged for garbage collection.

Your 20 iterations will generate 20 stack allocations, each of which would have the value 20 in the stack with nothing generated in the heap. Upon exiting the method, the stack would be popped and the data lost.

Babak Naffas
  • 12,395
  • 3
  • 34
  • 49
  • "uninitialized (garbage) memory location" - No, it'll be a reference to `null` initially. – Enigmativity Oct 27 '17 at 00:00
  • "generate a pointer in your stack" - No, this technically doesn't happen. It'll generate a "reference", but not a "pointer". It could be that the implementation of the run-time uses pointers, but that's not in the spec. – Enigmativity Oct 27 '17 at 00:02
  • "will generate 20 stack allocations" - not always. It'll probably be on the stack, but it can be elsewhere. – Enigmativity Oct 27 '17 at 00:04
  • > "in the Microsoft implementation of C# on the desktop CLR, value types are stored on the stack when the value is a local variable or temporary that is not a closed-over local variable of a lambda or anonymous method, and the method body is not an iterator block, and the jitter chooses to not enregister the value." – Enigmativity Oct 27 '17 at 00:04
  • "create a memory allocation in the heap to store the string itself" - also no. Because this string exists at compile time the string will be created in the intern pool and is not allocated on the heap. – Enigmativity Oct 27 '17 at 00:11
1

For any .NET value type variable like int, bool, double, etc.; the memory allocation happens as soon as you declare it and when you assign value to it, the value is just updated in memory.

For reference types including string, on the other hand, only an address is assigned in memory which creates a reference to the actual memory location where the current value is stored (similar to pointers in C/C++).

So, in your example, age will be created in memory as soon as the int age is run and then it's value will be set to 20 when the age = 20 gets executed.

It'll be assigned a new memory location each time the Run() method is executed.

Yatin
  • 1,178
  • 11
  • 15
  • 4
    "For any .NET value type variable like int, bool, double, etc.; the memory allocation happens as soon as you declare it" - Not entirely true - closures over value types, for example, don't work this way. In fact, it's like this: "in the Microsoft implementation of C# on the desktop CLR, value types are stored on the stack when the value is a local variable or temporary that is not a closed-over local variable of a lambda or anonymous method, and the method body is not an iterator block, and the jitter chooses to not enregister the value." – Enigmativity Oct 26 '17 at 23:59
  • @Enigmativity And don't forget boxing. – Joel Coehoorn Oct 27 '17 at 00:46
0

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.

Enigmativity
  • 113,464
  • 11
  • 89
  • 172
-1

In your first example (uninitialized variable), it will not allocate any memory since it won't generate any MSIL. It will be the same as no code at all. If you initialize it, memory will be allocated in the current method's stack.

Second case, the age variable will be allocated in the stack for each method call and should be released when each method call exits.

Mateus Schneiders
  • 4,853
  • 3
  • 20
  • 40
  • 2
    actually, in first example it will allocate - a variable on stack, one object reference, as soon as that method is invoked. It's small in size, but still it is an allocation, so it's not 'zero'. – quetzalcoatl Oct 26 '17 at 23:56
  • 1
    Furthermore, interestingly enough, at `foo = "mom"`, "mom" is compile-time constant, and probably the "mom" string object will already exist as interned in string pool when this code is invoked, so no new objects will be created and just the reference will be written to the variable on stack, which was allocated previously, so few bytes copied, no allocation. If the line was something like `foo = new string(5, 'x')` or `foo = new List` then it's a normal case and that would be allocate+construct+assign – quetzalcoatl Oct 27 '17 at 00:00
  • ah yes. and as **Enigmativity** mentioned in another comment, there's also JIT and possibility that a variable will be put into register.. eh – quetzalcoatl Oct 27 '17 at 00:03
  • @quetzalcoatl Interesting, I tested on linqpad, with only the variable declaration, without initialization, it didn't generate any MSIL. Maybe linqpad is not a good reference for this. – Mateus Schneiders Oct 27 '17 at 00:06
  • sorry, I quietly assumed that we're talking about eager delcaration of variables that are used lower down in the code. Like `string foo; ...... foo = "mom";`. Stack variable will be allocated immediately upon method call, not at the point of assignement. In your case, with unused variable, the compiler simply optimized it out. It cannot be done for unused class members, but on the stack compiler can easily omit unused things. – quetzalcoatl Oct 27 '17 at 00:10
  • @quetzalcoatl, to be honest, my answer was indeed ambiguous, I updated it to make it clearer. Thanks. – Mateus Schneiders Oct 27 '17 at 00:11