2010-08-22 8 views
10

mientras leía "C# en profundidad" Estaba repasando la sección "Los tipos de referencia viven en el montón, los tipos de valores viven en la pila".Los tipos de referencia viven en el montón, los tipos de valor viven en la pila

Ahora lo que pude entender es (principalmente para el tipo ref):

class Program 
{ 
    int a = 5; // stored in heap 

    public void Add(int x, int y) // x,y stored in stack 
    { 
     int c = x + y; // c stored in stack 
    } 
} 

Sólo quiero aclarar si mis suposiciones son correctas. Gracias. EDITAR: Debería haber usado variables diff, creo que creó una confusión. Así que he modificado el código.

EDITAR: Sí, como mencionó Jon - es un mito, debería haber mencionado eso.Mis disculpas.

Respuesta

13

http://blogs.msdn.com/b/ericlippert/archive/2009/04/27/the-stack-is-an-implementation-detail.aspx

enteros Los "tipos de referencia en el montón, los tipos de valor en la pila" no sólo es una mala manera de mirarlo, pero es demasiado malo.

+0

puede u PLS modificar el código y explicarte me refiero a un ejemplo de tipo de valor almacenado en el montón – Wondering

+0

No hay nada que modificar. En su código, a ya está almacenado en el montón como parte del objeto Programa. – siride

+0

por lo tanto, quiere decir que mis suposiciones son correctas. – Wondering

1

c deja en la pila porque, al menos, es un tipo de valor por su parte a en el montón administrado por ser el campo de tipo de referencia

+1

Tenga en cuenta que el valor de 'c' estaría en la pila (en las implementaciones actuales) incluso si fuera de tipo (por ejemplo)' StringBuilder'. Es solo que el valor de la variable sería una referencia a un objeto, es el * objeto * que estaría en el montón. Encuentro que hay muchas cosas más claras una vez que se distingue entre una variable, su valor y lo que ese valor realmente representa (por ejemplo, una referencia en lugar de un objeto real). –

+0

@Jon: ¡Gracias por esta nota! – abatishchev

8

puedo ser una abstracción algo útil tener una imagen mental de lo que está pasando detrás de la escenas. Pero ninguno de los dos es cierto en ninguna versión actualmente disponible de los compiladores JIT. Que quizás es el quid del problema, la ubicación de asignación real es un detalle de implementación del compilador JIT.

Existen al menos seis lugares donde un valor de tipo de valor puede vivir con la corriente principal (x86 y x64) nerviosismo:

  • en un marco de pila, puesto allí por una declaración de variable local o una llamada al método
  • en un registro de CPU, una optimización muy común realizada por el JIT en la versión Release. Y solía pasar argumentos a un método, los dos primeros x86, cuatro para x64. Y las variables locales cuando sea posible
  • en la pila FPU, utilizados por la fluctuación 86 de coma flotante de valores
  • en el montón GC, cuando el valor es parte de un tipo de referencia
  • en el montón del cargador del dominio de aplicación, cuando la variable se declara estática
  • en el almacenamiento local de subprocesos cuando la variable tiene el atributo [ThreadStatic].

Los objetos de tipo de referencia se asignan comúnmente en el montón de GC. Pero sé de una excepción específica, las cadenas internas producidas a partir de literales en el código fuente se asignan en el montón del cargador de AppDomain. Esto se comporta completamente como un objeto en tiempo de ejecución, excepto que no está vinculado al montón de GC, el recolector simplemente no puede verlo.

Dirigiéndose a su fragmento de código:

  • sí, "a" es probable que se almacenen en el montón GG
  • "x" siempre es pasado en un registro de la CPU en x86 y x64. "y" estará en un registro de CPU en x64, la pila en x86.
  • "c" es probable que no exista en absoluto, eliminado por el compilador JIT porque el código no tiene ningún efecto.
+0

¿Por qué el primer param 'x' estará en la pila y el segundo' y' - no siempre? PD 'c' se eliminará en el modo de lanzamiento – abatishchev

+0

Dos registros de CPU para un núcleo x86, cuatro para un núcleo x64. El puntero "this" requiere uno. –

0

Las ubicaciones de almacenamiento (variables, campos, elementos de matriz, etc.) de tipos de referencia mantienen referencias a objetos en el montón; las ubicaciones de almacenamiento de los tipos de valores primitivos mantienen su valor dentro de ellos mismos; las ubicaciones de almacenamiento de los tipos de estructuras contienen todos sus campos, cada uno de los cuales puede ser un tipo de referencia o valor, dentro de ellos mismos. Si una instancia de clase contiene dos cadenas diferentes no nulas, un punto y un entero, las coordenadas X e Y del punto, así como el entero independiente y las referencias a las dos cadenas, se mantendrán dentro de un montón objeto. Cada una de las cadenas se llevará a cabo en un diferente objeto de montón. El punto clave sobre las ubicaciones de almacenamiento de las clases frente a las estructuras es que excepto en el caso de una entidad de clase que contiene una referencia a sí mismo, cada campo de tipo de referencia no nulo dentro de una clase o estructura contendrá una referencia a otro objeto , que estará en el montón.

0

Piénselo en términos de C/C++.

Cada vez que hace algo "nuevo", o usa malloc, eso va en el montón, es decir, el "objeto" va al montón, el puntero se coloca en la pila dentro del alcance de la estructura (o función, que en realidad es solo otra estructura) de la que es parte. Si es una variable local o un tipo de referencia (puntero), va a la pila.

Para decirlo de otra manera, el> objeto < al que apunta el tipo de referencia está en el montón, solo el puntero está en la pila. Las pérdidas de memoria ocurren cuando el programa saca el puntero de la pila, pero la memoria en el montón no se ha liberado para su uso. ¿Cómo sabe qué memoria liberar si se pierde la referencia a su ubicación? Bueno, C/C++ no podía, tenías que hacerlo tú mismo antes de que la referencia saliera de la pila y se perdiera para siempre, pero es ahí donde los lenguajes modernos aparecen con su fantasía de "montón de basura". Todavía es preferible limpiar explícitamente cualquier memoria de montón que asignó que implícitamente dejándola para que la GC recoja, es "más barata" de esa manera (en términos de recursos de CPU).

0

Citando Jon Skeet de su famous blog sobre cómo y dónde se almacenan los tipos de referencia y de valor en una aplicación .NET:

la ranura de memoria para una variable se almacena en cualquiera de la pila o la montón. Depende del contexto en el que se declare:

  1. Cada variable local (es decir, una declarada en un método) se almacena en la pila. Eso incluye variables de tipo de referencia: la variable en sí es en la pila, pero recuerde que el valor de una variable de tipo de referencia es solo una referencia (o nulo), no el objeto en sí. Los parámetros del método también cuentan como variables locales, pero si se declaran con el modificador de ref, , no obtienen su propia ranura, sino que comparten una ranura con la variable utilizada en el código de llamada. Ver mi artículo sobre el parámetro para más detalles.
  2. Las variables de instancia para un tipo de referencia siempre están en el montón. Ahí es donde el objeto en sí mismo "vive".
  3. Las variables de instancia para un tipo de valor se almacenan en el mismo contexto que la variable que declara el tipo de valor. La ranura de memoria para la instancia contiene efectivamente las ranuras para cada campo dentro de la instancia . Eso significa (dados los dos puntos anteriores) que una variable struct declarada dentro de un método siempre estará en la pila, mientras que una variable struct que es un campo de instancia de una clase estará en el montón .
  4. Cada variable estática se almacena en el montón, independientemente de si está declarada dentro de un tipo de referencia o un tipo de valor. Solo hay una ranura en total, sin importar cuántas instancias se creen. (No hay ninguna instancia creada para que exista ). Los detalles de exactamente en qué montón viven las variables son complicados, pero se explican en detalle en un artículo de MSDN sobre el tema .
Cuestiones relacionadas