Por que o JavaScript limpa por nós, mas ainda assim devemos entender como funciona?
\n\nQuando você escreve em JavaScript, raramente pensa sobre o que acontece com as variáveis depois que elas saem do escopo. Ao contrário de C ou C++, onde você é obrigado a chamar manualmente free() ou delete, o JavaScript assume o trabalho pesado de limpeza de memória. Isso é conveniente — até o momento em que sua aplicação começa a "travar", consumir gigabytes de RAM ou cair inesperadamente com o erro Out of Memory.
Familiar? Então vamos entender como realmente funciona o gerenciamento de memória em JavaScript e o que é o coletor de lixo JavaScript. Spoiler: não é mágica, mas sim um algoritmo rigoroso que pode (e deve) ser considerado ao escrever código.
\n\nComo o JavaScript armazena dados: pilha e heap
\n\nAntes de falar sobre coleta de lixo, precisamos entender onde os dados vivem. Em JavaScript, existem dois armazenamentos principais:
\n\n- \n
- Pilha (Stack) — memória rápida, porém limitada. Lá são armazenados primitivos (números, strings, valores booleanos) e referências a objetos. Cada chamada de função cria um novo quadro na pilha. \n
- Heap — área de memória não estruturada, onde vivem objetos, arrays, funções e closures. É aqui que ocorre a principal luta pela memória. \n
Quando você cria um objeto:
\n\nlet user = { name: 'Иван', age: 30 };\n\n\nA referência user vai para a pilha, e o próprio objeto { name: 'Иван', age: 30 } — para o heap. Enquanto houver pelo menos uma referência ao objeto, ele está vivo. Quando não houver mais referências, o objeto se torna "lixo".
Coletor de lixo em JavaScript: o protagonista
\n\nO coletor de lixo em JavaScript (Garbage Collector, GC) é um processo em segundo plano que verifica periodicamente o heap e remove objetos inalcançáveis. Parece simples, mas na prática é um compromisso complexo entre velocidade de limpeza e desempenho do seu código.
\n\nAlgoritmo Mark-and-Sweep (Marcação e varredura)
\n\nOs motores modernos (V8 no Chrome, SpiderMonkey no Firefox) usam o algoritmo Mark-and-Sweep. Ele funciona em duas etapas:
\n\n- \n
- Mark (Marcação): O GC começa pelos objetos raiz (objeto global, funções atuais, variáveis locais). Ele percorre todas as referências a partir das raízes e marca cada objeto alcançável. \n
- Sweep (Varredura): Todos os objetos não marcados são considerados lixo e removidos. A memória é liberada. \n
Exemplo simplificado:
\n\nlet a = { data: 'A' };\nlet b = { data: 'B' };\na.ref = b; // a referencia b\nb.ref = a; // b referencia a\n\n// Agora temos uma referência cíclica\na = null; // a não aponta mais para o objeto\nb = null; // b não aponta mais para o objeto\n\n\nAntigamente (antes do Mark-and-Sweep), essas referências cíclicas causavam vazamentos. Mas o GC moderno entende: se ambos os objetos são inalcançáveis a partir da raiz, eles são lixo, mesmo que se referenciem mutuamente.
\n\nGerações de objetos: jovens e velhos
\n\nA maioria dos objetos vive pouco tempo. Por exemplo, variáveis temporárias dentro de funções. Por isso, o V8 divide o heap em gerações:
\n\n- \n
- Young Generation (Geração jovem): objetos novos. A coleta aqui ocorre com frequência e rapidez (Scavenge — algoritmo que copia objetos vivos para outra área). \n
- Old Generation (Geração velha): objetos que sobreviveram a várias coletas. Eles são verificados com menos frequência, mas o processo leva mais tempo. \n
Essa abordagem evita "congelar" a página durante uma verificação completa de todo o heap.
\n\nVazamentos de memória em JavaScript: como causá-los (e não fazer isso)
\n\nMesmo com um coletor de lixo inteligente, é possível criar situações em que a memória não é liberada. Aqui estão as causas mais comuns de vaz