¿Por qué las estructuras se almacenan en la stack mientras las clases se almacenan en el montón (.NET)?

Sé que una de las diferencias entre clases y estructuras es que las instancias struct se almacenan en la stack y las instancias de clase (objetos) se almacenan en el montón.

Dado que las clases y las estructuras son muy similares. ¿Alguien sabe la diferencia para esta distinción particular?

(editado para cubrir puntos en comentarios)

Para enfatizar: existen diferencias y similitudes entre los tipos de valores y los tipos de referencia, pero esas diferencias no tienen nada que ver con la stack frente al montón, y todo que ver con la semántica de la copia frente a la semántica de referencia. En particular, si hacemos:

Foo first = new Foo { Bar = 123 }; Foo second = first; 

Entonces, ¿están “primero” y “segundo” hablando de la misma copia de Foo ? o diferentes copias? Sucede que la stack es una forma conveniente y eficiente de manejar los tipos de valor como variables. Pero eso es un detalle de implementación.

(fin de editar)

Con respecto a todo lo que dice “los tipos de valores van a la stack” … los tipos de valores no siempre van en la stack;

  • si son campos en una clase
  • si están en caja
  • si son “variables capturadas”
  • si están en un bloque iterador

luego van al montón (los dos últimos son solo ejemplos exóticos del primero)

es decir

 class Foo { int i; // on the heap } static void Foo() { int i = 0; // on the heap due to capture // ... Action act = delegate {Console.WriteLine(i);}; } static IEnumerable Foo() { int i = 0; // on the heap to do iterator block // yield return i; } 

Además, Eric Lippert (como ya se señaló) tiene una excelente entrada de blog sobre este tema

En la práctica, es útil poder asignar memoria en la stack para algunos propósitos, ya que esas asignaciones son muy rápidas.

Sin embargo, vale la pena señalar que no hay garantía fundamental de que todas las estructuras se colocarán en la stack. Eric Lippert recientemente escribió una entrada de blog interesante sobre este tema.

Esa es una gran pregunta; No lo escribí en el artículo al que se vinculó Marc Gravell. Aquí está la segunda parte:

http://blogs.msdn.com/ericlippert/archive/2009/05/04/the-stack-is-an-implementation-detail-part-two.aspx

Cada proceso tiene un bloque de datos compuesto por dos segmentos de memoria asignados diferentes. Estos son stack y heap. Stack funciona principalmente como administrador de flujo de progtwig y guarda variables locales, parámetros y punteros de retorno (en el caso de regresar de la función de trabajo actual).

Las clases son muy complejas y, en su mayoría, muy grandes en comparación con tipos de valores como estructuras (o tipos básicos, enteros, caracteres, etc.). Dado que la asignación de astackmientos debe estar especializada en la eficiencia del flujo de progtwigs, no sirve un entorno óptimo para mantener objetos grandes

Por lo tanto, para saludar a ambas expectativas, esta architecture separada surgió.

La manera en que el comstackdor y el entorno de tiempo de ejecución manejan la administración de la memoria ha crecido durante un largo período de tiempo. La decisión de asignación de la memoria de stack frente a la memoria de montón tenía mucho que ver con lo que se podía conocer en tiempo de comstackción y lo que se podía conocer en el tiempo de ejecución. Esto fue antes de los tiempos de ejecución administrados.

En general, el comstackdor tiene un buen control de lo que hay en la stack, decide qué se limpia y cuándo se basa en las convenciones de llamadas. El montón por otro lado, era más como el salvaje oeste. El comstackdor no tenía un buen control de cuándo iban y venían las cosas. Al colocar los argumentos de función en la stack, el comstackdor puede crear un scope : ese scope se puede controlar durante el tiempo de vida de la llamada. Este es un lugar natural para poner tipos de valores, ya que son fáciles de controlar en comparación con los tipos de referencia que pueden distribuir las ubicaciones de memoria (punteros) a casi cualquier persona que deseen.

La gestión moderna de la memoria cambia mucho de esto. El tiempo de ejecución de .NET puede tomar el control de los tipos de referencia y el almacenamiento dynamic administrado a través de complejos algoritmos de recolección de basura y administración de memoria. Este es también un tema muy, muy profundo .

Te recomiendo que eches un vistazo a algunos textos sobre comstackdores: crecí en Aho, así que lo recomiendo . También puede aprender mucho sobre el tema leyendo Gosling .

En algunos idiomas, como C ++, los objetos también son tipos de valores.

Encontrar un ejemplo para lo contrario es más difícil, pero bajo las estructuras de unión clásicas de Pascal solo se podía crear una instancia en el montón. (las estructuras normales pueden ser estáticas)

En resumen: esta situación es una elección, no una ley dura. Como C # (y Java antes) carecen de fundamentos de procedimiento, uno puede preguntarse por qué necesita estructuras en absoluto.

La razón por la que está allí, es probablemente una combinación de necesitarlo para interfaces externas y tener un tipo complejo y ajustado (contenedor-). Uno que es más rápido que la clase. Y luego es mejor convertirlo en un tipo de valor.

Marc Gravell ya explicó maravillosamente la diferencia con respecto a cómo se copian el valor y los tipos de referencia, que es la principal diferenciación entre ellos.

En cuanto a por qué los tipos de valor generalmente se crean en la stack, es porque la forma en que se copian lo permite. La stack tiene algunas ventajas definidas sobre el montón en términos de rendimiento, particularmente porque el comstackdor puede calcular la posición exacta de una variable creada en un cierto bloque de código, lo que hace que el acceso sea más rápido.

Cuando crea un tipo de referencia, recibe una referencia al objeto real que existe en el montón. Hay un pequeño nivel de indirección cada vez que interactúa con el objeto en sí. Estos tipos de referencia no se pueden crear en la stack porque la vida de los valores en la stack está determinada, en gran parte, por la estructura de su código. El cuadro de función de una llamada a un método se eliminará de la stack cuando la función vuelva, por ejemplo.

Sin embargo, con los tipos de valor, su semántica de copia le permite al comstackdor, dependiendo de dónde se creó, colocarlo en la stack. Si crea una variable local que contiene una instancia de una estructura en un método y luego la devuelve, se creará una copia, como Marc explicó anteriormente. Esto significa que el valor puede colocarse de manera segura en la stack, ya que la duración de la instancia real está vinculada al marco de función del método. Cada vez que lo envíe a algún lugar fuera de la función actual se creará una copia, por lo que no importa si relaciona la existencia de la instancia original con el scope de la función. En esta línea, también puede ver por qué los tipos de valores que son capturados por los cierres deben ir en el montón: ellos sobreviven su scope porque también deben ser accesibles desde dentro del cierre, que se puede pasar libremente.

Si fuera un tipo de referencia, no devolvería una copia del objeto, sino una referencia, lo que significa que el valor real debe almacenarse en otro lugar; de lo contrario, si devolvió la referencia y la duración del objeto estuvo ligada a el scope en el cual fue creado, terminaría apuntando a un espacio vacío en la memoria.

La distinción no es realmente que “Tipos de valores van en la stack, tipos de referencia en el montón”. El verdadero punto es que generalmente es más eficiente acceder a los objetos que viven en la stack, por lo que el comstackdor intentará y colocará los valores que pueda. Simplemente resulta que los tipos de valores, debido a su semántica de copia, se ajustan mejor a la factura que los tipos de referencia.

Creo que si usar o no el espacio en stacks o montón es la principal distinción entre los dos, quizás este artículo arroje algo de luz sobre su pregunta: Clases de Csharp contra estructuras

La principal diferencia es que el montón puede contener objetos que viven para siempre, mientras que algo en la stack es temporal en el sentido de que desaparecerá cuando se cierre el sitio de llamada adjunto. Esto se debe a que cuando uno ingresa un método, crece para contener variables locales así como también el método de la persona que llama. Cuando el método sale (ab) normalmente, por ejemplo, de retorno o debido a una excepción, cada cuadro debe salir de la stack. Finalmente, el marco interesado aparece y todo se pierde.

El punto sobre el uso de la stack es que implementa automáticamente y respeta el scope. Una variable almacenada en la stack existe hasta que la función que la creó sale y esa función astack el marco de la stack. Las cosas que tienen scope local son naturales para el almacenamiento de la stack. Las cosas que tienen un scope mayor son más difíciles de administrar en la stack. Los objetos en el montón pueden tener vidas controladas de formas más complejas.

Los comstackdores siempre usan la stack para las variables: valor o referencia, no hace mucha diferencia. Una variable de referencia no tiene que tener su valor almacenado en la stack; puede estar en cualquier lugar y el montón lo hace más eficiente si el objeto al que se hace referencia es grande y si hay múltiples referencias a él. El punto es que el scope de una variable de referencia no es el mismo que el tiempo de vida del objeto al que hace referencia, es decir, una variable puede ser destruida si se saca de la stack, pero el objeto (en el montón) al que hace referencia podría vivir.

Si un tipo de valor es lo suficientemente pequeño, también podría almacenarlo en la stack en lugar de una referencia a él en el montón: su duración está vinculada al scope de la variable. Si el tipo de valor es parte de un tipo de referencia más grande, también podría tener múltiples referencias a él y, por lo tanto, es más natural almacenarlo en el montón y disociar su tiempo de vida de cualquier variable de referencia única.

La stack y el montón tienen una duración aproximada y la semántica de referencia de valor v es casi un subproducto.

Echa un vistazo a Valor y Referencia

Los tipos de valores van en la stack, los tipos de referencia van en el montón. Una estructura es un tipo de valor.

Sin embargo, no hay garantías sobre esto en la especificación, por lo que podría cambiar en futuros lanzamientos 🙂