¿Por qué los tipos de valores se almacenan en Stacks?

¿Por qué C # (.Net) prefiere la stack para almacenar tipos de valores? ¿Cuál es la razón principal detrás de este diseño? ¿Es porque las operaciones de lectura / escritura en la stack aprovechan mejor el procesador de la máquina?

Además, tal vez puedas justificar por qué otros no?

Eric Lippert discute esto aquí ; en primer lugar, es incorrecto que “los tipos de valores estén almacenados en la stack”. A veces lo son, pero no como:

  • campos en una clase
  • variables capturadas
  • variables en un bloque iterador

Cuando se pueden almacenar en la stack, es una forma conveniente de modelar su vida útil, pero no es necesario almacenarlos en la stack. Podría escribir un comstackdor + CLI que no tenga una stack, por ejemplo.

C # no almacena nada en la stack. C # es un lenguaje de progtwigción. Por lo tanto, una versión más correcta de su pregunta es ¿por qué el comstackdor C # de Microsoft emite instrucciones CIL para asignar tipos de valor en la stack?

Bueno, primero, solo algunas veces. Lo siguiente no va en la stack:

  1. Tipos de valores que son campos en una clase
  2. Tipos de valores en caja
  3. Tipos de valores locales que son variables externas de métodos anónimos
  4. Tipos de valores locales que son variables externas de bloques iteradores

En segundo lugar, cuando es posible, está hecho porque es eficiente. Básicamente en el modelo de memoria CLR, en la stack, la desasignación es un relativo muy barato en comparación con la desasignación en el montón. Con los locales de tipos de valor, puede estar seguro de que nadie más, excepto el local, hará referencia a la memoria para que pueda salirse con la suya utilizando la stack en lugar del montón. Para más detalles, vea Eric Lippert .

Finalmente, lo que hace que los tipos de valor sean especiales es que tienen semántica de tipo de valor (copia por valor), y no que a veces se asignen en la stack. No hay ningún requisito en la especificación C # de que el comstackdor emita instrucciones para asignar tipos de valores en la stack. Lo que la especificación de C # requiere es que los tipos de valor tengan semántica de tipo de valor.

Como @Akash indica, principalmente tiene que ver con la memoria. Durante el diseño del CLR, se observó (supongo que de la experiencia con Java) que la representación de tipos pequeños y primitivos como objetos con asas sometidas al recolector de basura causó una gran cantidad de seguimiento. Entonces los diseñadores querían un objeto “ligero” que no necesitara ser rastreado.

No hay un requisito específico en la especificación de CLI para que las primitivas se asignen en stack; es un artefacto de la implementación en la máquina. El bit esencial es que el tiempo de ejecución sabe dónde se deben las instancias a la construcción de patrones de memoria bien definidos (denominados marcos) más que en el índice de objetos asignados del GC. En las máquinas x86 (y similares), esto se puede hacer de manera eficiente utilizando la stack.

Tu statement no es totalmente cierta. Una versión mejor: C # almacena variables locales en la stack.
Y esto no es especial ni nuevo, (casi) todos los lenguajes de progtwigción usan la stack para las variables locales y las direcciones de retorno de métodos. Hay soporte para esto hasta el hardware.

Además, los Valuetypes pueden ser variables locales o campos dentro de los tipos de referencia. Entonces, los tipos de valores no siempre se almacenan en la stack. Una statement más útil: los tipos de referencia nunca se almacenan en la stack.

Así que no te concentres en la stack demasiado, es un detalle de implementación. Aprende sobre valores y tipos de referencia .

La operación de stack o la operación de Heap, ambas serán las mismas que está accediendo a una dirección de memoria que están en dos ubicaciones diferentes.

Los tipos de valores son pequeños, int, byte, etc., son de pequeño tamaño y se los referencia con mucha frecuencia en términos de cálculos matemáticos. Dado que son de un tamaño muy pequeño, de 4 a 16 bytes como máximo, (no debe usar más de 16 bytes en el tipo de valor para obtener el mejor rendimiento), asignar ese pequeño espacio en el montón y desasignar, recoger basura, etc. sería muy costoso.

Cada método que ingrese, en promedio definimos 10 tipos de valores locales para usarlo internamente, lo que será muy costoso en el montón como tipos de referencia.

La stack puede crecer y reducirse fácilmente (¡no el tamaño de la stack, sino la porción de stack utilizada para el Método actual!) Ya que los tipos de valor se tratan como desplazamiento desde el puntero de stack, y su asignación y desasignación es fácil como su simple incremento y decremento en stackpointer por tamaño total de todos los tipos de valores utilizados.

Donde más en el tipo de referencia, cada objeto de referencia tiene su propia asignación y tamaño, además CLR tiene que mantener la tabla de objetos que es una especie de índice de punteros reales en la memoria para evitar desbordamientos de búfer. Por lo tanto, un objeto que utilice (tipo de referencia) en realidad tiene dos almacenamiento, una entrada de índice en la tabla de referencia de CLR y espacio de memoria real. Por eso es fácil y rápido almacenar tipos de valores en la stack.

Para una operación adecuada del progtwig, es importante que tanto las entidades de tipo valor como las de tipo clase superen cualquier referencia a ellas. Cuando se crea un objeto de clase, se crea una referencia que puede copiarse libremente en cualquier ámbito. En consecuencia, es completamente posible que las referencias al objeto continúen existiendo incluso después de que salga el scope actual. Por el contrario, cuando se crea una variable de tipo valor, las únicas referencias que se pueden crear son de tipo efímero que desaparecerán antes de que salga el scope actual. El hecho de que no pueda existir ninguna referencia a una variable de tipo valor cuando sale el scope actual hace que sea seguro almacenar dichas variables en una stack.