为什么值类型存储在堆栈中?

发布于 2024-08-15 14:41:35 字数 104 浏览 8 评论 0原文

为什么 C# (.Net) 更喜欢使用堆栈来存储值类型?这种设计背后的主要原因是什么?是因为对堆栈的读/写操作更好地利用了机器处理器吗?

另外,也许你可以证明为什么其他人不可以?

Why does C# (.Net) prefer stack to store value types? What is the primary reason behind this design? Is it because read/write operations to the stack take better advantage of the machine processor?

Also, maybe you can justify why not others?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

瀟灑尐姊 2024-08-22 14:41:35

Eric Lippert 讨论了这个这里;首先,“值类型存储在堆栈上”是不正确的。它们有时是这样的,但不是这样的:

  • 类上的字段
  • 捕获变量
  • 迭代器块中的变量

当它们可以存储在堆栈上时,这是对它们的生命周期进行建模的便捷方法,但要求将它们存储在堆栈上。例如,您可以编写一个没有堆栈的编译器+CLI。

Eric Lippert discusses this here; firstly, it is incorrect that "value types are stored on the stack". They sometimes are, but not as:

  • fields on a class
  • captured variables
  • variables in an iterator block

When they can be stored on the stack it is a convenient way of modelling their lifetime, but it isn't required to store them on the stack. You could write a compiler+CLI that doesn't have a stack, for example.

因为看清所以看轻 2024-08-22 14:41:35

C# 不在堆栈上存储任何内容。 C# 是一种编程语言。因此,您的问题的更正确版本是为什么 Microsoft C# 编译器发出 CIL 指令在堆栈上分配值类型?

嗯,首先,它只是有时如此。以下内容不会进入堆栈:

  1. 作为类中字段的值
  2. 类型 装箱
  3. 值类型 作为匿名方法的外部变量的本地值
  4. 类型 作为迭代器块的外部变量的本地值类型

其次,如果可能的话,会这样做,因为它很高效。基本上在 CLR 内存模型中,堆栈上的释放相对于堆上的释放来说非常便宜。对于值类型的局部变量,您可以确保除了局部变量之外没有其他人会引用内存,因此您可以使用堆栈而不是堆。有关详细信息,请参阅 埃里克·利珀特

最后,值类型的特殊之处在于它们具有值类型语义(按值复制),而不是它们有时在堆栈上分配。 C# 规范中没有要求编译器发出在堆栈上分配值类型的指令。 C# 规范确实要求值类型具有值类型语义。

C# doesn't store anything on the stack. C# is a programming language. Therefore, a more correct version of your question is why does the Microsoft C# compiler emit CIL instructions to allocate value types on the stack?

Well, first, it only does sometimes. The following do not go on the stack:

  1. Value types that are fields in a class
  2. Boxed value types
  3. Local value types that are outer variables of anonymous methods
  4. Local value types that are outer variables of iterator blocks

Second, when it's possible it's done because it's efficient. Basically in the CLR memory model, on the stack deallocation is very cheap relative compared to deallocation on the heap. With locals of value types, you can be sure that no one else except the local will reference the memory so you can get away with using the stack instead of the heap. For details, see Eric Lippert.

Finally, what makes value types special is that they have value-type semantics (copy by value), not that they are sometimes allocated on the stack. There is no requirement in the C# specification that the compiler emit instructions to allocate value types on the stack. What the C# specification does require is that value-types have value-type semantics.

最偏执的依靠 2024-08-22 14:41:35

正如@Akash 所指出的,这主要与内存有关。在 CLR 的设计过程中,有人指出(我的猜测是根据 Java 的经验),将小型原始类型表示为带有句柄的对象,并接受垃圾收集器的处理,这会导致大量的跟踪开销。因此设计师想要一个不需要被跟踪的“轻量级”物体。

CLI 规范中对于要分配堆栈的原语没有具体要求;它是机器上实现的产物。重要的是,运行时知道实例在哪里,这是由于明确定义的内存模式(称为帧)的构造,而不是在 GC 的已分配对象索引中。在 x86(和类似)机器上,可以使用堆栈有效地完成此操作。

As @Akash indicates, it mostly has to do with memory. During the design of the CLR, it was noted (my guess was from the experience with Java) that representing small, primitive types as objects with handles subjected to the garbage collector caused a lot of tracking overhead. So the designers wanted a "lightweight" object which didn't need to be tracked.

There is no specific requirement in the CLI specification for primitives to be stack allocated; it's an artifact of the implementation on the machine. The essential bit is that the runtime knows where the instances are due to the construction of well-defined patterns of memory (called frames) rather than in the GC's index of allocated objects. On x86 (and similar) machines, this can be done efficiently using the stack.

你的笑 2024-08-22 14:41:35

你的说法并不完全正确。更好的版本:C# 将局部变量存储在堆栈上。
这并不特殊或新颖,(几乎)所有编程语言都使用堆栈来存储局部变量和方法返回地址。硬件方面对此都有支持。

此外,值类型可以是局部变量或引用类型内的字段。所以值类型并不总是存储在堆栈上。一个更有用的说法:引用类型从不存储在堆栈上。

所以不要太关注堆栈,它是一个实现细节。了解值类型和引用类型

Your statement isn't totally true. A better version: C# stores local variables on the stack.
And this isn't special or new, (almost) all programming languages use the stack for local variables and method return addresses. There is support for this down to the hardware.

Furthermore, Valuetypes can be local variables or fields inside reference types. So valuetypes are not always stored on the stack. A more helpful statement: referencetypes are never stored on the stack.

So don't focus on the stack too muuch, it's an implementation detail. Learn about value- and reference-types.

黎歌 2024-08-22 14:41:35

堆栈操作或堆操作,它们都与您访问位于两个不同位置的内存地址相同。

值类型有small、int、byte等,它们的大小很小,并且在数学计算中被频繁引用。由于它们的大小非常小,最多 4 到 16 个字节(为了获得最佳性能,您不应在值类型中使用超过 16 个字节),因此在堆上分配如此小的空间并取消分配、垃圾收集等将非常昂贵。

我们输入的每个方法平均都会定义 10 个本地值类型在内部使用,作为引用类型,这在堆上的成本非常高。

堆栈可以轻松增长和收缩(不是堆栈大小,而是用于当前方法的堆栈部分!),因为值类型只是作为堆栈指针的偏移量进行寻址,并且它们的分配和释放很容易,因为它在堆栈指针上简单地递增和递减按使用的所有值类型的总大小。

在引用类型的其他地方,每个引用对象都有自己的分配和大小,而且 CLR 必须维护对象表,该表类似于内存中实际指针的索引,以避免缓冲区溢出。所以你使用的一个对象(引用类型)实际上有两个存储空间,一个是CLR引用表中的索引项,一个是实际的内存空间。这就是为什么可以轻松快速地在堆栈上存储值类型。

Stack operation or Heap operation, they both will be same as you are accessing a memory address which are in two different locations.

Value types are small, int, byte etc, they are small in size and they are referenced very frequently in terms of mathematical computations. Since they are very small in size, 4 to 16 bytes max, (you should not use more then 16 bytes in value type for best performance), alloating such small space on heap and deallocating, garbage collection etc would be very costly.

Every method we enter, on an average we define 10 local value types to use it internally, that will be very costly on heap as reference types.

Stack can grow and shrink easily (not the stack size, but the stack portion used for current Method !!) as the valuetypes are just addressed as offset from stack pointer, and their allocation and deallocation is easy as its simple increment and decrememnt on stackpointer by total size of all valuetypes used.

Where else in reference type, each reference object has its own allocation and sizing, plus CLR has to maintain table of objects which is sort of like index of actual pointers in memory in order to avoid buffer overflows. So one object you use (reference type) actually has two storage, one index entry in CLR's reference table, and actual memory space. Thats why its easy and fast to store value types on stack.

你怎么敢 2024-08-22 14:41:35

为了正确的程序操作,重要的是值类型和类类型实体的寿命比对它们的任何引用都长。当创建类类型对象时,会创建一个可以自由复制到任何范围的引用。因此,即使在当前作用域退出之后,对该对象的引用也完全有可能继续存在。相比之下,当创建值类型变量时,可以创建的唯一引用是短期类型,该类型将在当前作用域退出之前消失。当当前作用域退出时,不能存在对值类型变量的引用,这一事实使得将此类变量存储在堆栈上是安全的。

For proper program operation, it is important that both value-type and class-type entities outlive any references to them. When a class-type object is created, a reference is created which may be freely copied into any scope. Consequently, it's entirely possible that references to the object will continue to exist even after the present scope exits. By contrast, when a value-type variable is created, the only references that can be created are of a short-lived type that will disappear before the current scope exits. The fact that no reference can exist to a value-type variable when the present scope exits makes it safe to store such variables on a stack.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文