Java Stack vs Heap: Guia Prático de Alocação de Memória
O que é Memória Stack?
Em Java, a stack é uma região de memória contígua que armazena frames de chamadas de método, variáveis locais e variáveis de referência. Cada vez que um método é invocado, um novo frame é empilhado na stack. A memória stack é organizada em Last-In, First-Out (LIFO), significando que a chamada mais recente é a primeira a ser removida quando o método retorna.
O que é Memória Heap?
O heap é a área onde objetos Java são criados. Armazena todas as instâncias de classes e, opcionalmente, variáveis de referência que apontam para esses objetos. Diferente da stack, o heap não está ligado ao tempo de vida de uma única chamada de método, permitindo que objetos persistam além do escopo onde foram criados.
Alocação de Memória em Java
Alocação de memória em Java refere-se ao processo de reservar memória virtual para dados do programa. Variáveis não são alocadas na declaração; em vez disso, uma referência é criada. Quando o operador new é usado, a JVM aloca espaço no heap para o objeto.
Java divide memória em quatro seções distintas para gerenciamento eficiente:
- Heap
- Stack
- Code (Bytecode)
- Static (Dados de classe)
- A seção Code contém o bytecode compilado.
- A Stack armazena frames de método, variáveis locais e variáveis de referência.
- O Heap armazena objetos e, ocasionalmente, variáveis de referência.
- A seção Static mantém campos e métodos estáticos.
Variáveis Locais vs. Variáveis de Instância
Variáveis de instância são declaradas dentro de uma classe mas fora de qualquer método:
class Aluno {
int num; // variável de instância
public void showData() {}
}
Variáveis locais são declaradas dentro de um método (ou como parâmetros de método):
public void soma(int a) {
int x = a + 3; // a e x são variáveis locais
}
Stack vs. Heap em Ação
Vamos percorrer um exemplo para ilustrar como stack e heap interagem durante chamadas de método.
Assuma que o método main chama m1():
public void m1() {
int x = 20;
}
Um frame para m1 é empilhado na stack. A variável local x é armazenada neste frame.
Quando m1 chama m2(int b), um novo frame é adicionado sobre o de m1:
public void m2(int b) {
boolean c;
}
Similarmente, m2 chama m3(), criando outro frame:
Dentro de m3, um objeto Conta é instanciado no heap, enquanto sua referência ref vive na stack:
class Conta {
int p;
int q;
}
public void m3() {
Conta ref = new Conta(); // código adicional
}
A variável de referência ref é criada no frame da stack de m3 e aponta para o objeto Conta alocado no heap.
Quando m3 completa, seu frame da stack é desempilhado, deixando o objeto Conta sem referências. Torna-se elegível para garbage collection.
O mesmo processo repete para m2 e m1, retornando controle ao main.
Referências de Objeto como Variáveis de Instância
Considere uma classe A que mantém referência a uma instância de B:
public static void main(String[] args) {
A pai = new A(); // código adicional
}
class A {
B filho = new B();
int e;
}
class B {
int c;
int d;
}
Aqui, a referência filho é armazenada no heap como parte do objeto A, apontando para sua própria instância B.
Principais Conclusões
- Cada chamada de método cria um novo frame na stack.
- Variáveis locais residem na stack; variáveis de instância estão no heap.
- Variáveis de referência são armazenadas na stack mas apontam para objetos no heap.
- Quando um frame da stack é desempilhado, suas variáveis locais e de referência são descartadas, e quaisquer objetos órfãos no heap tornam-se elegíveis para garbage collection.
Java
- Variáveis e Literais em Java: Guia Completo
- Classes e Objetos em Java: Guia Prático
- Classes e Métodos Abstratos em Java: Guia Completo
- Dominando Classes Aninhadas e Internas em Java: Tipos, Exemplos e Boas Práticas
- Java throw vs throws: Dominando Tratamento de Exceções com Exemplos Práticos
- Tipos de Anotação em Java: Guia Completo de Predefinidas, Customizadas e Meta-Anotações
- Dominando Autoboxing e Unboxing em Java: Exemplos Práticos e Boas Práticas
- Dominando Substituição de Strings em Java: replace(), replaceAll() e replaceFirst() Explicados
- Sobrescrita de Métodos em Java: Personalizando Comportamento da Superclasse
- Java 10: Alocação de Heap em NV-DIMM com -XX:AllocateHeapAt