DEV Community

Henrique Reis
Henrique Reis

Posted on

Stack memory e Heap memory

Embora esses dois tipos de memórias se refiram a formas diferentes de alocar e gerenciar dados durante a execução de um programa, entender como funcionam ajuda a prever o desempenho e possíveis comportamentos inesperados do código em desenvolvimento.

O compilador tem acesso direto à stack memory, já que ela é usada para armazenar variáveis locais e dados cujo o tamanho é conhecido em tempo de compilação (significa que o compilador sabe exatamente quantos bytes aquele dado vai ocupar antes do programa começar a rodar, ou seja, o tamanho da variável é fixa e previsível), o que garante maior eficiência.

Por outro lado, a heap memory é utilizada para armazenar dados que precisam permanecer acessíveis além do escopo atual ou cujo o tamanho só é conhecido em tempo de execução. Nela ficam os valores "reais" apontados por variáveis de referência, como objetos, arrays e estruturas dinâmicas.


Stack Memory

A stack memory é responsável por armazenar tipos por valor. Mas o que são tipos por valor?

Tipos por valor são aqueles em que a variável guarda diretamente o valor em si, e não uma referência para ele.

Exemplos comuns de tipos por valor: number, boolean, char, struct, int, float.

let a = 10;
let b = a;
b = 20;
console.log(a); // 10 => não foi afetado
Enter fullscreen mode Exit fullscreen mode

Mas stack memory, também é uma estrutura de dados LIFO (Last in, First out), onde armazena dados de curta duração, como:

  • Chamadas de funções (frames da stack)
  • Variáveis primitivas (valores por valor, como mencionado anteriormente)
  • Referências para objetos que estão na heap

Algumas características são:

  1. Rápida, alocação e desalocação ocorrem automaticamente, ajustando apenas o stack pointer.
  2. Limitada, espaço pequeno (em torno de ~1MB por thread no Node.js por exemplo, podendo variar por sistema).
  3. Limpeza automática quando uma função termina (os frames são removidos da pilha).
  4. Armazena variáveis locais, parâmetros de função e endereços de retorno.
  5. Quando a pilha antige seu limite, ocorre o erro conhecido como "stack overflow".

Heap Memory

Já a heap memory é responsável por armazenar tipos por referência.

Tipos por referência são aqueles em que a variável guarda o endereço (referência) do valor na memória, e não o valor em sí.

Quando uma variável é copiada, ambas passam a apontar para o mesmo dado, e alterar uma afeta a outra.

Tipos dos dados mais comuns utilizados por referência são: object, array, function(em JS), classes ou ponteiros.

let a = { x: 10 };
let b = a;
b.x = 20;
console.log(a.x); // 20 => ambos apontam para o mesmo objeto
Enter fullscreen mode Exit fullscreen mode

A heap é uma região da memória usada para armazenar dados dinâmicos, cujo o tamanho ou tempo de vida não são conhecidos em tempo de compilação. Nela ficam os valores reais referenciados pelas variáveis da stack.

Algumas características:

  1. Armazena objetos, arrays e funções.
  2. Permite alocação dinâmica de memória.
  3. É mais lenta que a stack, pois envolve gerenciamento de memória e ponteiros.
  4. É gerenciada automaticamente por um Garbage Collector (em linguagens como JavaScript, Java, Go)
  5. Os dados permanecem na memória até não serem mais referenciados.

E quando um dado deixa de ser referencido?

Um dado (objeto, array ou função) deixa de ser referenciado quando nenhuma variável ou estrutura ativa do programa aponta mais para ele na memória.

Em outras palavras: não existe mais nenhum caminho, a partir do código em execução, que leve até aquele valor na heap.

Quando isso acontece, o garbage collector entende que o valor não pode mais ser acessado e libera a memória automaticamente, um exemplo bem básico:

let user = { name: 'John'}
// A variável user referencia o objeto na heap

user = null
// Agora não existe mais nenhuma referência para o objeto { name: 'John' }
Enter fullscreen mode Exit fullscreen mode

O objeto { name: 'John' } ainda existe fisicamente na heap mas como nenhuma variável aponta para ele, o garbage collector irá removê-lo em algum momento. Agora um outro exemplo interessante de perda de referência:

let obj1 = { x: 10 };
let obj2 = obj1;
obj1 = null; 
// Ainda há uma referência ativa (obj2), então o dado continua na heap

obj2 = null; 
// Agora não há mais nenhuma — o dado é elegível para coleta
Enter fullscreen mode Exit fullscreen mode

Stack + Heap em conjunto (para assimilar mais rápido)

Durante a execução de um programa, a stack e a heap trabalham lado a lado par armazenar e gerenciar dados de forma eficiente.

A stack guarda as referências e valores simples (por valor), enquanto a heap armazena os valores complexos (por referência)

Quando uma variável na stack aponta para um objeto ou array, o endereço de memória desse objeto fica salvo na stack, mas o conteúdo real está na heap (como foi explicado anteriormente), exemplo:

function createUser() {
  const user = { name: "Henrique", age: 25 };
  return user;
}

const newUser = createUser();
Enter fullscreen mode Exit fullscreen mode
  • A função createUser cria uma variável user na stack.
  • Essa variável guarda uma referência para um objeto armazenado na heap.
  • Quando a função termina, a stack é liberada, mas o objeto na heap permanece se ainda estiver sendo referenciado (newUser aponta para ele por exemplo).

Blank Space

Blank space é um espaço entre a stack memory e a heap memory, que é utilizada para expansão de uma das memórias caso haja necessidade (é interessante saber que existe caso queiram se aprofundar mais sobre o assunto).


Espero que esta explicação tenha ajudado a entender de forma prática o papel da stack e da heap, e como eles impactam a performance e o funcionamento do código. Ainda há muito a explorar sobre esse tema, como o funcionamento do Garbage Collector, otimizações de memória e outros detalhes que merecem posts e estudos dedicados da minha parte. Para quem quiser se aprofundar, certamente vale a pena.

Top comments (0)