25

很久以前,我想知道变量(值类型或引用类型)将存储在哪里。它会在堆栈还是堆上?

我也读过Eric Lippert 的文章

出于好奇,我想要交叉验证我所理解的内容。任何工具都存在吗?或者我会以任何方式知道,在执行 .NET 程序时,哪些变量会存储在堆栈中?哪个存储在堆上?

谢谢

4

1 回答 1

72

考虑按堆栈和堆划分存储是一种方便的抽象,可以很好地为您服务。但更复杂的是,.NET 程序中有 6 个不同的变量存储位置。

这里选择的工具是调试器,它可以准确地显示变量存储的位置。这确实需要深入了解机器代码的工作原理。使用 Debug + Windows + Disassembly 查看机器码。查看程序的发布版本并更改允许即使在调试代码时也能优化代码的设置也很重要。工具 + 选项,调试,常规,取消勾选“在模块加载时抑制 JIT 优化”选项。您现在将看到机器代码在您的用户机器上执行的方式。

你必须事先知道的事情才能理解这一切:

  • 引用类型的对象存储在 GC 堆上。存储引用的变量具有与值类型值相同的存储选择。

  • 值类型值或对象引用有六个可能的存储位置:

    • 如果变量是引用类型的成员,它们将存储在 GC 堆中
    • 如果变量声明为静态,则它们存储在 AppDomain 的加载器堆中
    • 如果变量是 [ThreadStatic],它们将存储在线程本地存储中
    • 如果变量是方法参数或局部变量,它们可以存储在堆栈帧中
    • 如果变量是方法参数或局部变量,它们可以存储在 CPU 寄存器中
    • 针对 x86 抖动,可以将 Single 或 Double 类型的变量存储在 FPU 堆栈中。

后三个要点是它变得复杂的地方,以及为什么您需要查看机器代码来找出它们的存储位置。它是高度实现特定的,抖动的类型很重要。并且受您是否启用抖动优化器的影响很大。在这里做出正确的选择对性能非常重要。粗略的轮廓(跳过 ARM 抖动):

  • 前两个方法参数存储在 x86 抖动的 CPU 寄存器中,包括实例方法的this的值。x64 抖动使用 4 个寄存器。浮点处理器寄存器用于在 x86 上传递 Single 和 Double 类型的变量,在 x64 上传递 XMM 寄存器

  • 函数返回值在 CPU 寄存器中返回,如果它适合,则使用 E​​AX 或 RAX 寄存器,如果它是浮点值,则返回 ST0。如果它不适合,则调用者在堆栈帧上为该值保留空间并传递一个指向它的指针

  • 抖动优化器寻找机会将局部变量存储在 CPU 寄存器中。如果由于寄存器用完而被迫这样做,它可能会将寄存器溢出回堆栈帧。

这些实现细节有许多可观察到的副作用:

  • 获取存储在 cpu 寄存器中的局部变量会使代码难以调试。调试器对存储位置的了解不够。这是 Debug 构建存在的主要原因,它抑制了优化,因此您可以轻松检查局部变量,调试器确实知道用于变量的堆栈帧槽
  • 您无法检查方法的返回值,这给调试带来了极大的不便。调试器对抖动选择的存储位置知之甚少,无法可靠地找到该值。编辑:在 VS2013 中修复
  • 由于变量被优化以存储在 cpu 寄存器中,因此您可能难以调试线程问题。在循环或 if() 语句中测试值会产生寄存器中值的副本,而不是存储在内存中的值。特别是 x86 抖动问题和volatile关键字的原因,该关键字抑制了这种优化
  • 您可以初始化指向局部变量的指针,而无需固定它。与存储在 GC 堆中的变量可能会被垃圾收集移动并因此需要固定不同,局部变量具有固定的存储地址,该地址在整个方法体中都有效
  • 为堆栈帧分配的空间量由抖动决定。然而,可以自己分配一个块,C# stackalloc关键字支持它。这是您可以直接分配的最快内存
  • Having floating point values stored in an FPU register causes floating point accuracy problems. While it is stored in the FPU, a value is stored with 80 bit precision. But when it is spilled to memory, it is truncated to 32 or 64 bit precision. The unpredictability of when this spill occurs (plus the different strategy of the x64 jitter) produces floating point results that can be different with small changes producing large differences in the calculation result if the calculation loses a lot of significant digits.
于 2012-12-24T16:33:06.380 回答