Su matriz se asigna en el montón y las entradas no están encuadradas.
La fuente de su confusión es probable porque la gente ha dicho que los tipos de referencia se asignan en el montón y los tipos de valor se asignan en la pila. Esta no es una representación completamente precisa.
Todas las variables y parámetros locales se asignan en la pila. Esto incluye tanto los tipos de valor como los tipos de referencia. La diferencia entre los dos es solo lo que se almacena en la variable. Como era de esperar, para un tipo de valor, el valor del tipo se almacena directamente en la variable, y para un tipo de referencia, el valor del tipo se almacena en el montón, y una referencia a este valor es lo que se almacena en la variable.
Lo mismo vale para los campos. Cuando se asigna memoria para una instancia de un tipo agregado (a class
o a struct
), debe incluir almacenamiento para cada uno de sus campos de instancia. Para los campos de tipo de referencia, este almacenamiento contiene solo una referencia al valor, que luego se asignaría en el montón. Para los campos de tipo de valor, este almacenamiento contiene el valor real.
Entonces, dados los siguientes tipos:
class RefType{
public int I;
public string S;
public long L;
}
struct ValType{
public int I;
public string S;
public long L;
}
Los valores de cada uno de estos tipos requerirían 16 bytes de memoria (suponiendo un tamaño de palabra de 32 bits). El campo I
en cada caso toma 4 bytes para almacenar su valor, el campo S
toma 4 bytes para almacenar su referencia y el campo L
toma 8 bytes para almacenar su valor. Entonces la memoria para el valor de ambos RefType
y se ValType
ve así:
0 ┌───────────────────┐
│ yo │
4 ├───────────────────┤
│ S │
8 ├───────────────────┤
│ L │
│ │
16 └───────────────────┘
Ahora bien, si usted tenía tres variables locales de una función, de tipos RefType
, ValType
y int[]
, de esta manera:
RefType refType;
ValType valType;
int[] intArray;
entonces su pila podría verse así:
0 ┌───────────────────┐
│ refType │
4 ├───────────────────┤
│ valType │
│ │
│ │
│ │
20 ├───────────────────┤
│ intArray │
24 └───────────────────┘
Si asignó valores a estas variables locales, así:
refType = new RefType();
refType.I = 100;
refType.S = "refType.S";
refType.L = 0x0123456789ABCDEF;
valType = new ValType();
valType.I = 200;
valType.S = "valType.S";
valType.L = 0x0011223344556677;
intArray = new int[4];
intArray[0] = 300;
intArray[1] = 301;
intArray[2] = 302;
intArray[3] = 303;
Entonces su pila podría verse así:
0 ┌───────────────────┐
X4 0x4A963B68 │ - dirección de montón de `refType`
4 ├───────────────────┤
│ 200 │ - valor de `valType.I`
X4 0x4A984C10 │ - dirección del montón de `valType.S`
X4 0x44556677 │ - bajo 32 bits de `valType.L`
│ 0x00112233 │ - 32 bits altos de `valType.L`
20 ├───────────────────┤
X4 0x4AA4C288 │ - dirección de montón de `intArray`
24 └───────────────────┘
La memoria en la dirección 0x4A963B68
(valor de refType
) sería algo así como:
0 ┌───────────────────┐
│ 100 │ - valor de `refType.I`
4 ├───────────────────┤
│ 0x4A984D88 │ - dirección de montón de `refType.S`
8 ├───────────────────┤
│ 0x89ABCDEF │ - bajo 32 bits de `refType.L`
│ 0x01234567 │ - 32 bits altos de `refType.L`
16 └───────────────────┘
La memoria en la dirección 0x4AA4C288
(valor de intArray
) sería algo así como:
0 ┌───────────────────┐
│ 4 │ - longitud de la matriz
4 ├───────────────────┤
│ 300 │ - `intArray [0]`
8 ├───────────────────┤
│ 301 │ - `intArray [1]`
12 ├───────────────────┤
│ 302 │ - `intArray [2]`
16 ├───────────────────┤
│ 303 │ - `intArray [3]`
20 └───────────────────┘
Ahora, si pasó intArray
a otra función, el valor introducido en la pila sería 0x4AA4C288
, la dirección de la matriz, no una copia de la matriz.
Sí, la matriz se ubicará en el montón.
Las entradas dentro de la matriz no se encuadrarán. El hecho de que exista un tipo de valor en el montón no significa necesariamente que estará encuadrado. El boxeo solo ocurrirá cuando se asigne un tipo de valor, como int, a una referencia de objeto de tipo.
Por ejemplo
No en caja:
Cajas:
También puede consultar la publicación de Eric sobre este tema:
fuente
Para entender lo que está sucediendo, aquí hay algunos hechos:
Por lo tanto, si tiene una matriz de enteros, la matriz se asigna en el montón y los enteros que contiene es parte del objeto de matriz en el montón. Los enteros residen dentro del objeto de matriz en el montón, no como objetos separados, por lo que no están encuadrados.
Si tiene una matriz de cadenas, es realmente una matriz de referencias de cadena. Como las referencias son tipos de valor, formarán parte del objeto de matriz en el montón. Si coloca un objeto de cadena en la matriz, en realidad coloca la referencia al objeto de cadena en la matriz, y la cadena es un objeto separado en el montón.
fuente
Creo que en el centro de su pregunta se encuentra un malentendido sobre los tipos de referencia y valor. Esto es algo con lo que probablemente lucharon todos los desarrolladores de .NET y Java.
Una matriz es solo una lista de valores. Si se trata de una matriz de un tipo de referencia (por ejemplo, a
string[]
), la matriz es una lista de referencias a variosstring
objetos en el montón, ya que una referencia es el valor de un tipo de referencia. Internamente, estas referencias se implementan como punteros a una dirección en la memoria. Si desea visualizar esto, una matriz de este tipo se vería así en la memoria (en el montón):[ 00000000, 00000000, 00000000, F8AB56AA ]
Esta es una matriz de
string
que contiene 4 referencias astring
objetos en el montón (los números aquí son hexadecimales). Actualmente, solo el últimostring
realmente apunta a algo (la memoria se inicializa a todos los ceros cuando se asigna), esta matriz básicamente sería el resultado de este código en C #:La matriz anterior estaría en un programa de 32 bits. En un programa de 64 bits, las referencias serían dos veces más grandes (
F8AB56AA
serían00000000F8AB56AA
).Si tiene una matriz de tipos de valores (digamos an
int[]
), la matriz es una lista de enteros, ya que el valor de un tipo de valor es el valor en sí mismo (de ahí el nombre). La visualización de tal matriz sería esta:[ 00000000, 45FF32BB, 00000000, 00000000 ]
Esta es una matriz de 4 enteros, donde solo al segundo int se le asigna un valor (a 1174352571, que es la representación decimal de ese número hexadecimal) y el resto de los enteros sería 0 (como dije, la memoria se inicializa a cero y 00000000 en hexadecimal es 0 en decimal). El código que produjo esta matriz sería:
Esta
int[]
matriz también se almacenaría en el montón.Como otro ejemplo, la memoria de una
short[4]
matriz se vería así:[ 0000, 0000, 0000, 0000 ]
Como el valor de a
short
es un número de 2 bytes.Cuando se almacena un tipo de valor, es solo un detalle de implementación, como Eric Lippert explica muy bien aquí , no es inherente a las diferencias entre los tipos de valor y referencia (que es la diferencia en el comportamiento).
Cuando pasa algo a un método (ya sea un tipo de referencia o un tipo de valor), una copia del valor del tipo se pasa realmente al método. En el caso de un tipo de referencia, el valor es una referencia (piense en esto como un puntero a una pieza de memoria, aunque eso también es un detalle de implementación) y en el caso de un tipo de valor, el valor es la cosa misma.
El boxeo solo ocurre si convierte un tipo de valor en un tipo de referencia. Este código recuadros:
fuente
Estas son ilustraciones que representan la respuesta anterior de @P Daddy
E ilustré los contenidos correspondientes en mi estilo.
fuente
Se asigna una matriz de enteros en el montón, nada más y nada menos. Las referencias de myIntegers al comienzo de la sección donde se asignan las entradas. Esa referencia se encuentra en la pila.
Si tiene una matriz de objetos de tipo de referencia, como el tipo de Objeto, myObjects [], ubicado en la pila, haría referencia al conjunto de valores que hacen referencia a los objetos mismos.
En resumen, si pasa myIntegers a algunas funciones, solo pasa la referencia al lugar donde se asigna el grupo real de enteros.
fuente
No hay boxeo en su código de ejemplo.
Los tipos de valor pueden vivir en el montón como lo hacen en su conjunto de entradas. La matriz se asigna en el montón y almacena entradas, que resultan ser tipos de valor. El contenido de la matriz se inicializa a default (int), que resulta ser cero.
Considere una clase que contiene un tipo de valor:
La variable h se refiere a una instancia de HasAnInt que vive en el montón. Simplemente contiene un tipo de valor. Eso está perfectamente bien, 'yo' simplemente sucede que vive en el montón ya que está contenido en una clase. No hay boxeo en este ejemplo tampoco.
fuente
Todo el mundo ha dicho lo suficiente, pero si alguien está buscando una muestra clara (pero no oficial) y documentación sobre el montón, la pila, las variables locales y las variables estáticas, consulte el artículo completo de Jon Skeet sobre Memoria en .NET: ¿qué pasa? dónde
Extracto:
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í está en la pila, pero recuerde que el valor de una variable de tipo de referencia es solo una referencia (o nula), 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 referencia, no obtienen su propio espacio, sino que comparten un espacio con la variable utilizada en el código de llamada. Vea mi artículo sobre el paso de parámetros para más detalles.
Las variables de instancia para un tipo de referencia siempre están en el montón. Ahí es donde el objeto mismo "vive".
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 de estructura declarada dentro de un método siempre estará en la pila, mientras que una variable de estructura que es un campo de instancia de una clase estará en el montón.
Cada variable estática se almacena en el montón, independientemente de si se declara dentro de un tipo de referencia o un tipo de valor. Solo hay un espacio en total, sin importar cuántas instancias se creen. (Sin embargo, no es necesario que se cree ninguna instancia para que exista esa ranura). 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.
fuente