Skip to main content
added 790 characters in body
Source Link
Hans Passant
  • 946.3k
  • 151
  • 1.8k
  • 2.6k

It is primarily the JIT compiler that determines where local variables are stored. It is a heavy architecture implementation detail, let's limit it to the x86 jitter. Common choices it makes:

  • nowhere. Which will happen with the very simple example you gave, the jitter optimizer can see that a local variable is initialized but not used anywhere and will eliminate it.

  • in a CPU register. That's a very important optimization, no storage location is faster. A variable will almost always live inside a register at least part of the time when the method body executes, necessary because a lot of cpu instructions require the operand to be present in a register first. The jitter will only spill the variable to the stack frame if it runs out of registers (x86 doesn't have many) and needs to re-use a register for another operation

  • in the stack frame for the method. The traditional way everybody thinks about it. A variable is stored at a fixed offset from the EBP register. Accessing those variables is very fast, not quite as fast as when they are stored in a register.

I however also have to talk about ways that the compiler affects the storage location for variable that has local scope in the language (thanks Marc):

  • on the garbage collected heap. This is done by the compiler when it rewrites the code to implement an iterator, captures variables for anonymous methods or lambda expressions or implements methods marked with the async keyword. The local variables become fields of a hidden class that gets allocated on the heap as normal.

  • in the loader heap. Outlandish to a C# programmer but supported by the VB.NET compiler with the Static keyword. A feature that's otherwise implemented by the compiler, not the jitter. Itit acts like a C# static field but with the scope limited to the method body. With lots of auto-generated code to ensure it is initialized correctly, even when called from threads.

Which covers almost all possible storage locations for a variable :) Although I have trouble coming up with a [ThreadStatic] example. This is a possible case of information overload, focus on bullets 2 and 3 for the most common ways. And certainly bullet 3 to think productively about the way managed code works.

It is the JIT compiler that determines where local variables are stored. It is a heavy architecture implementation detail, let's limit it to the x86 jitter. Common choices it makes:

  • nowhere. Which will happen with the very simple example you gave, the jitter optimizer can see that a local variable is initialized but not used anywhere and will eliminate it.

  • in a CPU register. That's a very important optimization, no storage location is faster. A variable will almost always live inside a register at least part of the time when the method body executes, necessary because a lot of cpu instructions require the operand to be present in a register first. The jitter will only spill the variable to the stack frame if it runs out of registers (x86 doesn't have many) and needs to re-use a register for another operation

  • in the stack frame for the method. The traditional way everybody thinks about it. A variable is stored at a fixed offset from the EBP register. Accessing those variables is very fast, not quite as fast as when they are stored in a register.

  • in the loader heap. Outlandish to a C# programmer but supported by the VB.NET compiler with the Static keyword. A feature that's otherwise implemented by the compiler, not the jitter. It acts like a C# static field but with the scope limited to the method body. With lots of auto-generated code to ensure it is initialized correctly, even when called from threads.

It is primarily the JIT compiler that determines where local variables are stored. It is a heavy architecture implementation detail, let's limit it to the x86 jitter. Common choices it makes:

  • nowhere. Which will happen with the very simple example you gave, the jitter optimizer can see that a local variable is initialized but not used anywhere and will eliminate it.

  • in a CPU register. That's a very important optimization, no storage location is faster. A variable will almost always live inside a register at least part of the time when the method body executes, necessary because a lot of cpu instructions require the operand to be present in a register first. The jitter will only spill the variable to the stack frame if it runs out of registers (x86 doesn't have many) and needs to re-use a register for another operation

  • in the stack frame for the method. The traditional way everybody thinks about it. A variable is stored at a fixed offset from the EBP register. Accessing those variables is very fast, not quite as fast as when they are stored in a register.

I however also have to talk about ways that the compiler affects the storage location for variable that has local scope in the language (thanks Marc):

  • on the garbage collected heap. This is done by the compiler when it rewrites the code to implement an iterator, captures variables for anonymous methods or lambda expressions or implements methods marked with the async keyword. The local variables become fields of a hidden class that gets allocated on the heap as normal.

  • in the loader heap. Outlandish to a C# programmer but supported by the VB.NET compiler with the Static keyword. A feature that's implemented by the compiler, it acts like a C# static field but with the scope limited to the method body. With lots of auto-generated code to ensure it is initialized correctly, even when called from threads.

Which covers almost all possible storage locations for a variable :) Although I have trouble coming up with a [ThreadStatic] example. This is a possible case of information overload, focus on bullets 2 and 3 for the most common ways. And certainly bullet 3 to think productively about the way managed code works.

Source Link
Hans Passant
  • 946.3k
  • 151
  • 1.8k
  • 2.6k

It is the JIT compiler that determines where local variables are stored. It is a heavy architecture implementation detail, let's limit it to the x86 jitter. Common choices it makes:

  • nowhere. Which will happen with the very simple example you gave, the jitter optimizer can see that a local variable is initialized but not used anywhere and will eliminate it.

  • in a CPU register. That's a very important optimization, no storage location is faster. A variable will almost always live inside a register at least part of the time when the method body executes, necessary because a lot of cpu instructions require the operand to be present in a register first. The jitter will only spill the variable to the stack frame if it runs out of registers (x86 doesn't have many) and needs to re-use a register for another operation

  • in the stack frame for the method. The traditional way everybody thinks about it. A variable is stored at a fixed offset from the EBP register. Accessing those variables is very fast, not quite as fast as when they are stored in a register.

  • in the loader heap. Outlandish to a C# programmer but supported by the VB.NET compiler with the Static keyword. A feature that's otherwise implemented by the compiler, not the jitter. It acts like a C# static field but with the scope limited to the method body. With lots of auto-generated code to ensure it is initialized correctly, even when called from threads.