Onde os campos primitivos são armazenados?
Os campos primitivos são armazenados como parte do objeto que é instanciado em algum lugar . A maneira mais fácil de pensar onde é isso - é a pilha. No entanto , esse nem sempre é o caso. Conforme descrito na teoria e na prática de Java: Lendas do desempenho urbano, revisitadas :
As JVMs podem usar uma técnica chamada análise de escape, pela qual eles podem dizer que certos objetos permanecem confinados a um único encadeamento por toda a vida útil e que a vida útil é limitada pela vida útil de um determinado quadro de pilha. Esses objetos podem ser alocados com segurança na pilha em vez da pilha. Melhor ainda, para objetos pequenos, a JVM pode otimizar completamente a alocação e simplesmente içar os campos do objeto em registros.
Assim, além de dizer "o objeto é criado e o campo também existe", não se pode dizer se algo está na pilha ou na pilha. Observe que, para objetos pequenos e de curta duração, é possível que o 'objeto' não exista na memória como tal e, em vez disso, tenha seus campos colocados diretamente nos registradores.
O artigo conclui com:
As JVMs são surpreendentemente boas em descobrir coisas que costumamos assumir que apenas o desenvolvedor poderia saber. Ao permitir que a JVM escolha entre alocação de pilha e alocação de heap caso a caso, podemos obter os benefícios de desempenho da alocação de pilha sem fazer com que o programador se agonize sobre a alocação na pilha ou no heap.
Portanto, se você tiver um código parecido com:
void foo(int arg) {
Bar qux = new Bar(arg);
...
}
onde o ...
não permite qux
deixar esse escopo, qux
pode ser alocado na pilha. Na verdade, isso é uma vitória para a VM, porque significa que ela nunca precisa ser coletada como lixo - ela desaparecerá quando sair do escopo.
Mais sobre a análise de escape na Wikipedia. Para aqueles que desejam se aprofundar em documentos, Escape Analysis for Java da IBM. Para quem vem de um mundo C #, pode encontrar boas leituras de A pilha é um detalhe de implementação e A verdade sobre os tipos de valor, por Eric Lippert (são úteis para os tipos Java, pois muitos dos conceitos e aspectos são iguais ou semelhantes) . Por que os livros .Net falam sobre alocação de memória pilha versus pilha? também entra nisso.
Nos porquês da pilha e da pilha
Na pilha
Então, por que ter a pilha ou a pilha? Para coisas que deixam o escopo, a pilha pode ser cara. Considere o código:
void foo(String arg) {
bar(arg);
...
}
void bar(String arg) {
qux(arg);
...
}
void qux(String arg) {
...
}
Os parâmetros também fazem parte da pilha. Na situação em que você não tem um heap, você passaria o conjunto completo de valores na pilha. Isso é bom para "foo"
pequenas strings ... mas o que aconteceria se alguém colocasse um arquivo XML enorme nessa string. Cada chamada copiava toda a cadeia enorme para a pilha - e isso seria um grande desperdício.
Em vez disso, é melhor colocar os objetos que têm alguma vida fora do escopo imediato (passados para outro escopo, presos em uma estrutura que outra pessoa está mantendo, etc ...) em outra área chamada heap.
Na pilha
Você não precisa da pilha. Poderia-se, hipoteticamente, escrever uma linguagem que não use uma pilha (de profundidade arbitrária). Um antigo BASIC que aprendi na juventude o fez, só era possível fazer 8 níveis de gosub
chamadas e todas as variáveis eram globais - não havia pilha.
A vantagem da pilha é que, quando você tem uma variável que existe com um escopo, quando você sai desse escopo, o quadro da pilha é exibido. Simplifica realmente o que está lá e o que não está lá. O programa passa para outro procedimento, um novo quadro de pilha; o programa retorna ao procedimento e você retornou ao que vê seu escopo atual; o programa deixa o procedimento e todos os itens da pilha são desalocados.
Isso realmente facilita a vida da pessoa que está escrevendo o tempo de execução do código para usar uma pilha e uma pilha. Eles simplesmente muitos conceitos e maneiras de trabalhar no código, permitindo que a pessoa que escreve o código na linguagem seja liberada de pensar neles explicitamente.
A natureza da pilha também significa que ela não pode se fragmentar. A fragmentação da memória é um problema real com a pilha. Você aloca alguns objetos, coleta o lixo do meio e tenta encontrar espaço para o próximo grande a ser alocado. É uma bagunça. Ser capaz de colocar as coisas na pilha significa que você não precisa lidar com isso.
Quando algo é coletado de lixo
Quando algo é coletado, o lixo se foi. Mas é apenas lixo coletado porque já foi esquecido - não há mais referências ao objeto no programa que podem ser acessadas a partir do estado atual do programa.
Vou apontar que essa é uma simplificação muito grande da coleta de lixo. Existem muitos coletores de lixo (mesmo no Java - você pode ajustá-lo usando vários sinalizadores ( docs ).) Eles se comportam de maneira diferente e as nuances de como cada um faz as coisas são um pouco profundas para esta resposta. Noções básicas sobre coleta de lixo de Java para ter uma idéia melhor de como isso funciona.
Dito isto, se algo estiver alocado na pilha, ele não será coletado como parte System.gc()
dele - será desalocado quando o quadro da pilha aparecer. Se algo estiver na pilha e referenciado a partir de algo na pilha, não será coletado lixo naquele momento.
Por que isso importa?
Para a maior parte, a sua tradição. Os livros de texto escritos e as classes de compilador e a documentação de vários bits são importantes para o heap e a pilha.
No entanto, as máquinas virtuais de hoje (JVM e similares) fizeram um grande esforço para tentar manter isso escondido do programador. A menos que você esteja ficando sem um e outro e precise saber o motivo (em vez de apenas aumentar o espaço de armazenamento adequadamente), isso não importa muito.
O objeto está em algum lugar e está no local em que pode ser acessado corretamente e rapidamente pela quantidade adequada de tempo em que ele existe. Se estiver na pilha ou na pilha - isso realmente não importa.