Sua matriz está alocada no heap e as entradas não estão na caixa.
A origem da sua confusão é provável porque as pessoas disseram que os tipos de referência são alocados no heap e os tipos de valor são alocados na pilha. Esta não é uma representação totalmente precisa.
Todas as variáveis e parâmetros locais são alocados na pilha. Isso inclui tipos de valor e tipos de referência. A diferença entre os dois é apenas o que é armazenado na variável. Não é novidade que, para um tipo de valor, o valor do tipo é armazenado diretamente na variável e, para um tipo de referência, o valor do tipo é armazenado no heap, e uma referência a esse valor é o que é armazenado na variável.
O mesmo vale para os campos. Quando a memória é alocada para uma instância de um tipo agregado (a class
ou a struct
), ela deve incluir armazenamento para cada um de seus campos de instância. Para campos do tipo de referência, esse armazenamento mantém apenas uma referência ao valor, que seria alocado no heap posteriormente. Para campos do tipo valor, esse armazenamento retém o valor real.
Portanto, considerando os seguintes tipos:
class RefType{
public int I;
public string S;
public long L;
}
struct ValType{
public int I;
public string S;
public long L;
}
Os valores de cada um desses tipos exigiriam 16 bytes de memória (assumindo um tamanho de palavra de 32 bits). O campo I
em cada caso leva 4 bytes para armazenar seu valor, o campo S
leva 4 bytes para armazenar sua referência e o campo L
leva 8 bytes para armazenar seu valor. Portanto, a memória para o valor de ambos RefType
e ValType
fica assim:
0 ┌───────────────────┐
│ eu │
4 ├───────────────────┤
│ S │
8 ├───────────────────┤
│ L │
│ │
16 └───────────────────┘
Agora, se você tivesse três variáveis locais em uma função, de tipos RefType
, ValType
e int[]
, como este:
RefType refType;
ValType valType;
int[] intArray;
sua pilha pode ficar assim:
0 ┌───────────────────┐
│ refType │
4 ├───────────────────┤
T valType │
│ │
│ │
│ │
20 ├───────────────────┤
│ intArray │
24 └───────────────────┘
Se você atribuiu valores a essas variáveis locais, assim:
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;
Então sua pilha pode ficar assim:
0 ┌───────────────────┐
│ 0x4A963B68 │ - endereço de pilha de `refType`
4 ├───────────────────┤
│ 200 │ - valor de `valType.I`
│ 0x4A984C10 │ - endereço de pilha de `valType.S`
│ 0x44556677 │ - baixos 32 bits de `valType.L`
│ 0x00112233 │ - alta de 32 bits de `valType.L`
20 ├───────────────────┤
│ 0x4AA4C288 │ - endereço de pilha de `intArray`
24 └───────────────────┘
A memória no endereço 0x4A963B68
(valor de refType
) seria algo como:
0 ┌───────────────────┐
│ 100 │ - valor de `refType.I`
4 ├───────────────────┤
│ 0x4A984D88 │ - endereço de pilha de `refType.S`
8 ├───────────────────┤
│ 0x89ABCDEF │ - baixos 32 bits de `refType.L`
│ 0x01234567 │ - alta de 32 bits de `refType.L`
16 └───────────────────┘
A memória no endereço 0x4AA4C288
(valor de intArray
) seria algo como:
0 ┌───────────────────┐
│ 4 │ - comprimento da matriz
4 ├───────────────────┤
│ 300 │ - `intArray [0]`
8 ├───────────────────┤
│ 301 │ - `intArray [1]`
12 ├───────────────────┤
│ 302 │ - `intArray [2]`
16 ├───────────────────┤
│ 303 │ - `intArray [3]`
20 └───────────────────┘
Agora, se você passasse intArray
para outra função, o valor enviado para a pilha seria 0x4AA4C288
o endereço da matriz, não uma cópia da matriz.
Sim, a matriz estará localizada na pilha.
As entradas dentro da matriz não serão colocadas em caixa. Só porque um tipo de valor existe na pilha, não significa necessariamente que ele será encaixotado. O boxe ocorrerá somente quando um tipo de valor, como int, for atribuído a uma referência do objeto de tipo.
Por exemplo
Não caixa:
Caixas:
Você também pode conferir a postagem de Eric sobre este assunto:
fonte
Para entender o que está acontecendo, aqui estão alguns fatos:
Portanto, se você tiver uma matriz de números inteiros, a matriz é alocada no heap e os números inteiros que ele contém fazem parte do objeto da matriz no heap. Os números inteiros residem dentro do objeto de matriz na pilha, não como objetos separados, portanto, eles não são encaixotados.
Se você tem uma matriz de strings, é realmente uma matriz de referências de strings. Como as referências são tipos de valor, elas farão parte do objeto de matriz na pilha. Se você colocar um objeto de string na matriz, na verdade, coloca a referência ao objeto de string na matriz, e a string é um objeto separado no heap.
fonte
Penso que no cerne da sua pergunta está um equívoco sobre os tipos de referência e valor. Provavelmente, isso é algo com o qual todos os desenvolvedores .NET e Java lutaram.
Uma matriz é apenas uma lista de valores. Se for uma matriz de um tipo de referência (digamos a
string[]
), a matriz é uma lista de referências a váriosstring
objetos no heap, pois uma referência é o valor de um tipo de referência. Internamente, essas referências são implementadas como ponteiros para um endereço na memória. Se você deseja visualizar isso, essa matriz seria assim na memória (na pilha):[ 00000000, 00000000, 00000000, F8AB56AA ]
Essa é uma matriz
string
que contém 4 referências astring
objetos no heap (os números aqui são hexadecimais). Atualmente, apenas o últimostring
realmente aponta para qualquer coisa (a memória é inicializada com todos os zero quando alocada), essa matriz seria basicamente o resultado desse código em C #:A matriz acima estaria em um programa de 32 bits. Em um programa de 64 bits, as referências seriam duas vezes maiores (
F8AB56AA
seriam00000000F8AB56AA
).Se você possui uma matriz de tipos de valor (digamos um
int[]
), a matriz é uma lista de números inteiros, pois o valor de um tipo de valor é o próprio valor (daí o nome). A visualização dessa matriz seria a seguinte:[ 00000000, 45FF32BB, 00000000, 00000000 ]
Esta é uma matriz de 4 números inteiros, em que apenas o segundo int recebe um valor (para 1174352571, que é a representação decimal desse número hexadecimal) e o restante dos números inteiros seria 0 (como eu disse, a memória é inicializada como zero e 00000000 em hexadecimal é 0 em decimal). O código que produziu essa matriz seria:
Essa
int[]
matriz também seria armazenada na pilha.Como outro exemplo, a memória de uma
short[4]
matriz ficaria assim:[ 0000, 0000, 0000, 0000 ]
Como o valor de a
short
é um número de 2 bytes.Onde um tipo de valor é armazenado, é apenas um detalhe de implementação, como Eric Lippert explica muito bem aqui , não inerente às diferenças entre os tipos de valor e referência (que é a diferença de comportamento).
Quando você passa algo para um método (seja um tipo de referência ou um tipo de valor), uma cópia do valor do tipo é realmente passada para o método. No caso de um tipo de referência, o valor é uma referência (pense nisso como um ponteiro para uma parte da memória, embora isso também seja um detalhe de implementação) e, no caso de um tipo de valor, o valor é a coisa em si.
O boxe ocorre apenas se você converter um tipo de valor em um tipo de referência. Este código caixas:
fonte
Estas são ilustrações que ilustram a resposta acima de @P Daddy
E ilustrei o conteúdo correspondente no meu estilo.
fonte
Uma matriz de números inteiros é alocada no heap, nada mais, nada menos. myIntegers faz referência ao início da seção em que as entradas são alocadas. Essa referência está localizada na pilha.
Se você tiver uma matriz de objetos do tipo de referência, como o tipo de objeto myObjects [], localizado na pilha, faria referência ao conjunto de valores que referenciam os objetos em si.
Para resumir, se você passar myIntegers para algumas funções, você só passará a referência para o local em que o monte real de números inteiros está alocado.
fonte
Não há boxe no seu código de exemplo.
Os tipos de valor podem viver no heap, como acontece na sua matriz de ints. A matriz é alocada no heap e armazena ints, que são tipos de valor. O conteúdo da matriz é inicializado como padrão (int), que passa a ser zero.
Considere uma classe que contém um tipo de valor:
A variável h refere-se a uma instância de HasAnInt que vive na pilha. Por acaso contém um tipo de valor. Tudo bem, 'i' simplesmente vive na pilha, pois ela está contida em uma classe. Também não há boxe neste exemplo.
fonte
Já foi dito por todos, mas se alguém está procurando uma amostra e documentação clara (mas não oficial) sobre heap, stack, variáveis locais e variáveis estáticas, consulte o artigo completo de Jon Skeet sobre Memória no .NET - o que acontece Onde
Excerto:
Cada variável local (isto é, uma declarada em um método) é armazenada na pilha. Isso inclui variáveis do tipo de referência - a própria variável está na pilha, mas lembre-se de que o valor de uma variável do tipo de referência é apenas uma referência (ou nula), não o próprio objeto. Os parâmetros do método também contam como variáveis locais, mas se forem declarados com o modificador ref, eles não terão seu próprio slot, mas compartilharão um slot com a variável usada no código de chamada. Veja meu artigo sobre a passagem de parâmetros para mais detalhes.
Variáveis de instância para um tipo de referência estão sempre no heap. É aí que o próprio objeto "vive".
As variáveis de instância para um tipo de valor são armazenadas no mesmo contexto que a variável que declara o tipo de valor. O slot de memória para a instância contém efetivamente os slots para cada campo dentro da instância. Isso significa (dados os dois pontos anteriores) que uma variável struct declarada em um método sempre estará na pilha, enquanto uma variável struct que é um campo de instância de uma classe estará na pilha.
Toda variável estática é armazenada no heap, independentemente de ser declarada em um tipo de referência ou um tipo de valor. Existe apenas um slot no total, não importa quantas instâncias sejam criadas. (Porém, não é necessário criar instâncias para que esse slot exista.) Os detalhes de exatamente em que pilha as variáveis vivem são complicados, mas explicados em detalhes em um artigo do MSDN sobre o assunto.
fonte