ebb*_*ebb 3 c# stack memory-management
void main()
{
int x = 5; // stack-allocated
Console.WriteLine(x);
}
Run Code Online (Sandbox Code Playgroud)
我知道这x是堆栈分配的.但是实际存储在堆栈中的是x什么?它是否具有"实际值",或者包含值的内存中的某个地址?
答案是响亮的"它取决于".
有一点是肯定的:局部变量的存储x(如果存在的话)包含实际值而不是引用,因为int它是值类型.
在简洁的脑translation翻译中,x(不是指针或对其的引用)的实际值存储在当前调用的激活记录中的局部变量中.生成的IL将是这样的:
.maxstack 1
.locals init (int32 V_0)
ldc.i4.5 // push 5 on the evaluation stack
stloc.0 // pop it into x
ldloc.0 // now push a copy of x to pass to ...
call void [mscorlib]System.Console::WriteLine(int32)
ret // return
Run Code Online (Sandbox Code Playgroud)
(注意,该.maxstack指令正在讨论IL评估堆栈,不一定与本机堆栈相对应.评估堆栈,如果浅,通常由JIT编译的代码保存在寄存器中.)
在CLR的大多数当前实现中,激活记录及其局部变量将存储在本机堆栈上.所以,天真地,我想x可以认为它的值存储在堆栈中.
在上述代码的优化编译(或优化JIT转换)中,IL中根本不存在局部变量存储.毕竟,为什么要分配局部变量存储来存储它,然后浪费两个使用该局部变量的指令,当你再也不需要它时?它可以作为常量参数内联到WriteLine:
.maxstack 1
// Just pass 5 to WriteLine. No local variables here.
ldc.i4.5
call void [mscorlib]System.Console::WriteLine(int32)
// Bye.
ret
Run Code Online (Sandbox Code Playgroud)
还有更多可能的并发症.如果函数是async在C#5 CTP 中声明的,或者x是由函数中其他地方的lambda表达式捕获的,或者函数是使用该yield return语句构建的枚举器,那么存储x可能会最终强制进入堆:它将成为编译器生成的类中的一个字段,这样如果闭包或延续转义本地范围,变量的存储可以在堆栈帧的拆除中继续存在.