.NET值类型和引用类型

时间:2012-07-21 19:02:27

标签: .net

“值类型是堆栈分配,而引用类型存在于托管堆上。”

如果我在一个类的方法中有一个局部变量(比如int a = 2;),它在哪里被分配?

在我们的示例中,值类型包含在引用类型中。由于引用存在于托管堆中,我假设此处的值类型(int a)也在托管堆中,而不是堆栈。

我在这里错过了什么吗?

2 个答案:

答案 0 :(得分:3)

主要是JIT编译器确定存储局部变量的位置。这是一个沉重的架构实现细节,让我们将其限制为x86抖动。它做出的共同选择:

  • 不通。通过你给出的一个非常简单的例子会发生这种情况,抖动优化器可以看到局部变量已初始化但未在任何地方使用,并将消除它。

  • CPU寄存器中的
  • 。这是一个非常重要的优化,没有存储位置更快。变量几乎总是在方法体执行时至少部分时间内存在于寄存器中,这是必要的,因为许多cpu指令要求操作数首先出现在寄存器中。如果变量用完寄存器(x86没有很多)并且需要重新使用寄存器进行其他操作,则抖动只会将变量溢出到堆栈帧

  • 在方法的堆栈框架中。每个人都想到的传统方式。变量存储在与EBP寄存器固定的偏移处。访问这些变量非常快,不如存储在寄存器中那么快。

然而,我还要讨论编译器如何影响具有语言本地范围的变量的存储位置(感谢Marc):

  • 在垃圾收集堆上。这是由编译器在重写代码以实现迭代器,捕获匿名方法或lambda表达式的变量或实现用 async 关键字标记的方法时完成的。局部变量成为隐藏类的字段,正常地在堆上分配。

  • 在加载程序堆中。与C#程序员不同,但VB.NET编译器支持Static关键字。由编译器实现的功能,它的作用类似于C#静态字段,但范围仅限于方法体。使用大量自动生成的代码来确保它正确初始化,即使从线程调用也是如此。

其中几乎涵盖了变量的所有可能存储位置:)虽然我无法想出[ThreadStatic]示例。这是信息过载的一种可能情况,最常见的方式是关注子弹2和3。当然,第3点要有效地思考托管代码的工作方式。

答案 1 :(得分:2)

首先,应该注意你的帖子的第一行是误导,不完整和不准确。价值类型几乎可以在任何地方使用。

  

在我们的示例中,值类型包含在引用类型中。

“包含在内”这里有误导性。你所混淆的“包含在内”是“实例字段”。这不适用于方法局部变量。方法局部变量,作为一个实现细节,存在于堆栈中......除非它们不存在!其中包括迭代器块和捕获的变量。既然你没有提到这些事情,那么答案可能是“在堆栈上”。

我还应该注意,即使对于引用类型的方法局部变量,变量(即引用,而不是对象)仍然存在于堆栈中(除了因为它没有,完全相同的规则)。

请注意,我将讨论局限于IL术语中发生的事情,即 C#编译器的内容。汉斯很正确地说JIT可能会在IL看到它的时候做任何想做的事情。