Espaço de Heap Java vs Stack – Alocação de Memória em Java

Há algum tempo, escrevi alguns posts sobre Coleta de Lixo em Java e Java é Passado por Valor. Depois disso, recebi muitos e-mails pedindo explicações sobre Espaço de Heap em Java, Memória de Pilha em Java, Alocação de Memória em Java e quais são as diferenças entre eles. Você verá muitas referências ao espaço de heap e memória de pilha em livros e tutoriais de Java e Java EE, mas dificilmente uma explicação completa do que é espaço de heap e memória de pilha em termos de um programa.

Espaço de Heap em Java

O espaço de heap em Java é usado pelo tempo de execução do Java para alocar memória para objetos e classes JRE. Sempre que criamos um objeto, ele é sempre criado no espaço de heap. A coleta de lixo é executada na memória de heap para liberar a memória usada por objetos que não têm referência. Qualquer objeto criado no espaço de heap tem acesso global e pode ser referenciado de qualquer parte do aplicativo.

Memória de Pilha em Java

Java Stack memory é usada para a execução de uma thread. Ela contém valores específicos do método que têm curta duração e referências a outros objetos no heap que estão sendo referenciados pelo método. A memória da pilha é sempre referenciada na ordem LIFO (Last-In-First-Out). Sempre que um método é invocado, um novo bloco é criado na memória da pilha para o método armazenar valores primitivos locais e referências a outros objetos no método. Assim que o método termina, o bloco se torna não utilizado e fica disponível para o próximo método. O tamanho da memória da pilha é muito menor em comparação com a memória do heap.

Heap e Stack Memory em Programas Java

Vamos entender o uso da memória da heap e da pilha com um programa simples.

package com.journaldev.test;

public class Memory {

	public static void main(String[] args) { // Line 1
		int i=1; // Line 2
		Object obj = new Object(); // Line 3
		Memory mem = new Memory(); // Line 4
		mem.foo(obj); // Line 5
	} // Line 9

	private void foo(Object param) { // Line 6
		String str = param.toString(); //// Line 7
		System.out.println(str);
	} // Line 8

}

A imagem abaixo mostra a memória da pilha e do heap em relação ao programa acima e como eles são usados para armazenar variáveis primitivas, objetos e variáveis de referência. Vamos seguir as etapas da execução do programa.

  • Ao executarmos o programa, ele carrega todas as classes Runtime no espaço do heap. Quando o método main() é encontrado na linha 1, o Java Runtime cria memória da pilha a ser usada pelo thread do método main().
  • Estamos criando uma variável local primitiva na linha 2, então ela é criada e armazenada na memória de pilha do método main().
  • Já que estamos criando um Objeto na 3ª linha, ele é criado na memória heap e a memória de pilha contém a referência para ele. Um processo semelhante ocorre quando criamos um objeto Memory na 4ª linha.
  • Agora, quando chamamos o método foo() na 5ª linha, um bloco no topo da pilha é criado para ser usado pelo método foo(). Como Java é passado por valor, uma nova referência ao Objeto é criada no bloco de pilha foo() na 6ª linha.
  • A string is created in the 7th line, it goes in the String Pool in the heap space and a reference is created in the foo() stack space for it.
  • O método foo() é encerrado na 8ª linha, neste momento o bloco de memória alocado para foo() na pilha se torna livre.
  • Na linha 9, o método main() é encerrado e a memória de pilha criada para o método main() é destruída. Além disso, o programa termina nesta linha, portanto, o Java Runtime libera toda a memória e encerra a execução do programa.

Diferença entre o Espaço de Heap e a Memória de Pilha do Java

Com base nas explicações acima, podemos facilmente concluir as seguintes diferenças entre a memória heap e a memória de pilha.

  1. A memória heap é usada por todas as partes da aplicação, enquanto a memória de pilha é usada apenas por uma thread de execução.
  2. Sempre que um objeto é criado, ele é sempre armazenado no espaço Heap, e a memória da pilha contém a referência a ele. A memória da pilha contém apenas variáveis primitivas locais e variáveis de referência a objetos no espaço Heap.
  3. Objetos armazenados no heap são globalmente acessíveis, enquanto a memória da pilha não pode ser acessada por outras threads.
  4. O gerenciamento de memória na pilha é feito de maneira LIFO, enquanto é mais complexo na memória Heap, pois é usada globalmente. A memória Heap é dividida em Young-Generation, Old-Generation, etc., mais detalhes em Coleta de Lixo em Java.
  5. A memória da pilha é de curta duração, enquanto a memória do heap persiste desde o início até o final da execução do aplicativo.
  6. Podemos usar as opções -Xms e -Xmx da JVM para definir o tamanho inicial e o tamanho máximo da memória do heap. Podemos usar -Xss para definir o tamanho da memória da pilha.
  7. Quando a memória da pilha está cheia, o tempo de execução do Java gera um erro java.lang.StackOverFlowError, enquanto se a memória do heap estiver cheia, ele gera um erro java.lang.OutOfMemoryError: Java Heap Space.
  8. O tamanho da memória da pilha é muito menor quando comparado à memória do heap. Devido à simplicidade na alocação de memória (LIFO), a memória da pilha é muito rápida em comparação com a memória do heap.

Isso é tudo para Espaço de Heap vs Memória da Pilha em Java em termos de aplicativos Java, espero que isso esclareça suas dúvidas em relação à alocação de memória quando qualquer programa Java é executado.

Referência: https://pt.wikipedia.org/wiki/Modelo_de_memória_Java.

Source:
https://www.digitalocean.com/community/tutorials/java-heap-space-vs-stack-memory