c# - 原始类型不变性和堆栈?

标签 c# stack primitive

我在阅读“通过 C# 实现 CLR”时,在阅读“事物在运行时的关系”和“原始类型、引用类型和值类型”时,我有点困惑。 如果我从 Main 调用了以下代码 -

void DoSomething(int x) 
{
  int m = x/2;
  int n = SomeMethod1(m);
      n = (n * 2) + x; 
  int k = SomeMethod2(n);
      m += (k*3);
}

该代码没有任何用处,但我只是想了解局部整数变量和内存分配的行为。

我了解 m、n 和 k 驻留在堆栈中? 现在,对于最后一行(我忽略了变量 n),必须修改“m”的值。所以堆栈上的所有其他东西都必须被弹出并更新'm'的值?与堆上装箱和拆箱的开销相比,这可以忽略不计吗?如果堆叠高度更高,它会变得重要吗?

P.S:我将 GC 排除在讨论之外(用于装箱和拆箱),这绝对是一种开销,而且这只是为了尝试理解行为,可能/可能不是实际场景。

最佳答案

不,没有人说 m、n 或 k 应该在堆栈上。最有可能的是,它们会在寄存器中,并且由于您甚至没有使用相互依赖性,它们甚至不必同时存在。 JIT 编译器非常擅长运行时优化,堆栈几乎不用于方法参数(或其引用)以外的任何东西。并且 GC 在寄存器或堆栈中都没有权力 - 堆栈的释放方式与 native 代码中的方式相同。只有堆是 GC 的一个域。

这是一个常见的误解,因为在 IL(C# 编译成的中间语言)中,实际上 一切都使用堆栈。但是,这不是在您的机器上执行的内容 - IL 代码由 JIT 编译器再次编译。当然,JIT 编译器也可以自由地处理堆栈上的所有事情,但那是愚蠢的。简单示例时间:

x + y

将编译成这个 IL(伪代码):

push y
push x
op_Add

所以对于一个简单的整数加法,需要进行一次方法调用和两次入栈。不是很贵,但如果你想做任何严肃的计算,你就有麻烦了。

但是,JIT 编译器会产生更像这样的东西:

add ax, bx

它在这方面非常聪明,所以它实际上经常将寄存器用于方法参数——如果它是安全的话。

但是请注意,所有这些都只是一个实现细节——在本例中是性能优化。整数可能很容易存在于堆上(事实上,如果它是装箱的或存在于堆上的另一个对象的一部分)。

因此,显然,最快的是 CPU 本身支持的那些 - 如上例所示,将两个寄存器加在一起。非常快。

使用栈通常还是很快的。并不是说分配给堆很昂贵——事实上,堆和堆栈分配的成本大致相同。伤害的是释放 - 堆需要进行垃圾收集和压缩,而堆栈只需要更改单个指针。

哦,您误解了访问堆栈的方式。的确,有基本的 pushpop 指令,但是,这些不是唯一的方式。您可以直接寻址堆栈的内存,就像寻址任何其他内存一样。事实上,这就是为什么您可以看到堆栈指针(如 ESP)的全部原因。

关于c# - 原始类型不变性和堆栈?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25781939/

相关文章:

c# - 如何将具有透明度的 PNG 转换为 GDI32(在 C# 中)以将其与 alphaBlend 一起使用?

c# - 静态 WCF 代理类对象

algorithm - 验证平衡括号的其他方法?

java - 有界基元

java - 为什么 BigInteger 不是原始类型

c# - 未连接控制台时,Console.WriteLine 是否会在 WinForms 应用程序中占用任何时间?

c# - 如何覆盖从构造函数调用的基类方法

c++ - 尝试编写一个 C++ 堆栈程序

python - Pandas 堆栈列对

Java - 当字符串不是文字而是对象时?