Memória e coletor de lixo em TypeScript: o que está por trás das cortinas?
\n\nImagine que você está construindo uma casa. Você compra tijolos, cimento, vergalhões — mas se não limpar os entulhos, o canteiro de obras logo se transformará em um lixão. O mesmo acontece com seu código em TypeScript. Você cria objetos, arrays, closures — e se não liberar memória a tempo, o aplicativo começa a travar e, em seguida, cai com o erro Out of Memory.
Neste artigo, vamos analisar como funciona o gerenciamento de memória em TypeScript e sua principal ferramenta — o coletor de lixo. Você aprenderá quais mecanismos estão por trás da liberação de memória, como evitar vazamentos e por que seu código pode "consumir" megabytes mesmo depois que você parou de usá-lo.
\n\nVamos lá!
\n\nComo o TypeScript gerencia a memória? Fundamentos
\n\nVamos começar com algo importante: TypeScript não é uma linguagem separada, mas uma extensão do JavaScript. Isso significa que a memória e o coletor de lixo em TypeScript funcionam exatamente como em JavaScript. TypeScript não adiciona novos mecanismos de gerenciamento de memória, mas nos dá ferramentas (tipos, interfaces) para escrever código mais seguro.
\n\nEm JavaScript, a memória é dividida em duas áreas principais:
\n\n- \n
- Pilha (Stack) — armazena primitivos (números, strings, valores booleanos) e referências a objetos. É liberada automaticamente ao sair de uma função. \n
- Heap (Heap) — armazena objetos, arrays, funções. É aqui que o coletor de lixo atua. \n
Quando você escreve:
\n\nconst user = { name: 'Alice', age: 30 };\n\nA variável user é armazenada na pilha e contém uma referência ao objeto no heap. Enquanto houver referência — o objeto está vivo. Assim que a referência desaparece — o objeto se torna "lixo".
Coletor de lixo em TypeScript: como ele funciona?
\n\nO coletor de lixo (Garbage Collector, GC) é um processo automático que encontra objetos para os quais não há mais referências e libera a memória ocupada por eles. Os motores modernos (V8 no Chrome, SpiderMonkey no Firefox) usam algoritmos complexos, mas a base é o algoritmo de marcação e varredura (Mark-and-Sweep).
\n\nComo funciona:
\n\n- \n
- O GC começa pelas "raízes" — objetos globais, variáveis locais em funções que estão sendo executadas. \n
- Ele percorre todas as referências a partir das raízes e marca cada objeto alcançável. \n
- Todos os objetos não marcados são considerados lixo e removidos. \n
Parece simples, mas na prática há nuances. Por exemplo, referências cíclicas:
\n\nfunction createCycle() {\n const obj1: any = {};\n const obj2: any = {};\n obj1.ref = obj2;\n obj2.ref = obj1;\n return 'done';\n}\n\nAntigamente, essas estruturas causavam vazamentos, mas os coletores de lixo modernos lidam muito bem com ciclos — o importante é que ambos os objetos sejam inalcançáveis a partir das raízes.
\n\nVazamentos de memória típicos em aplicações TypeScript
\n\nMesmo com um coletor de lixo inteligente, é possível "pisar no calo". Aqui estão os cenários mais comuns que levam a vazamentos de memória em TypeScript:
\n\n1. Timers e manipuladores de eventos esquecidos
\n\nUma das causas mais comuns. Você se inscreveu em um evento e esqueceu de cancelar a inscrição:
\n\nclass TimerComponent {\n private intervalId: number;\n\n start() {\n this.intervalId = window.setInterval(() => {\n console.log('Tick');\n }, 1000);\n }\n\n // Esqueceu de chamar stop() — o timer viverá para sempre!\n}\n\nSolução: sempre limpe timers e cancele inscrições de eventos no método dispose ou ngOnDestroy (para Angular).
2. Closures que mantêm referências
\n\nClosures são uma ferramenta poderosa, mas podem acidentalmente reter objetos enormes:
\n\nfunction createHeavyClosure() {\n const hugeData = new Array(1000