Memória e garbage collector em Kotlin: como não estourar a RAM e perder performance
\n\nVocê já se perguntou para onde vai a memória RAM da sua aplicação Kotlin? O código parece perfeito, as coroutines voam, mas depois de uma hora o programa começa a "travar" ou cai com OutOfMemoryError. Essa dor é familiar? Bem-vindo ao clube de quem quer entender como o Kotlin gerencia a memória e por que o garbage collector não é o vilão, mas sim seu melhor amigo (se você souber como lidar com ele).
Neste artigo, vamos mergulhar no tópico de gerenciamento de memória em Kotlin e analisar como funciona o garbage collector em Kotlin em diferentes plataformas. Você vai aprender o que são stack e heap, como evitar vazamentos de memória e por que o WeakReference pode salvar seu projeto. Vamos nessa!
O que é memória em Kotlin? Stack, Heap e seus amigos
\n\nKotlin é uma linguagem multiplataforma. Ela compila para bytecode JVM, código nativo (via Kotlin/Native) ou JavaScript (Kotlin/JS). E em cada plataforma, a memória funciona de forma um pouco diferente, mas a base é a mesma.
\n\nVamos relembrar o básico. Quando seu programa é iniciado, o sistema operacional aloca uma área de memória para ele. Dentro dessa área, existem duas "regiões" principais:
\n\n- \n
- Stack (Pilha) — aqui são armazenadas variáveis locais, tipos primitivos e referências a objetos. Funciona como uma pilha de pratos: rápido, previsível, mas com tamanho limitado. Ultrapassar o limite da pilha gera
StackOverflowError. \n - Heap (Monte) — o depósito para todos os objetos que você cria com
newou construtores (val list = mutableListOf()). O heap é grande, mas mais lento, e é nele que "mora" o garbage collector do Kotlin. \n
Na JVM, stack e heap funcionam de forma clássica. No Kotlin/Native, a stack é a pilha nativa da thread, e o heap é gerenciado por seu próprio garbage collector. No Kotlin/JS, tudo fica a cargo do motor JavaScript (V8, SpiderMonkey).
\n\nPonto-chave: as referências aos objetos ficam na stack, e os próprios objetos ficam no heap. Enquanto um objeto tiver pelo menos uma referência "viva" (da stack ou de outro objeto), o garbage collector não vai tocá-lo.
\n\nGarbage collector em Kotlin: como funciona na JVM
\n\nAo escrever em Kotlin para JVM (Android, servidor), você obtém o poder do Garbage Collector (GC) da Java Virtual Machine. Não é algo específico do Kotlin, mas entender sua mecânica é essencial.
\n\nA JVM moderna usa G1GC (Garbage First) ou ZGC (em versões mais novas). Como funciona de forma simplificada:
\n\n- \n
- Mark (Marcação) — o GC "percorre" todos os objetos vivos, começando pelos pontos raiz (stack, campos estáticos). Tudo que ele alcança está vivo. \n
- Sweep (Limpeza) — tudo que não foi marcado é considerado lixo e removido. \n
- Compact (Compactação) — os objetos vivos são movidos para evitar fragmentação. \n
No G1GC, o heap é dividido em regiões. O GC coleta as regiões com mais lixo (daí o nome). Isso reduz as pausas, mas não as elimina completamente.
\n\nNo Kotlin, você pode influenciar o garbage collector através de flags da JVM (para servidores) ou via GarbageCollectorMXBean (para monitoramento). Mas, na maioria das vezes, você só precisa escrever código que não atrapalhe o GC.
Exemplo: como o GC vê seu código
\n\nfun processData() {\n val list = mutableListOf<String>()\n for (i in 1..100_000) {\n list.add("Element $i")\n }\n // Aqui, list está vivo até o método terminar\n println(list.size)\n // Após sair do método, list se torna lixo\n}\n\n\nApós a conclusão de processData(), a referência a list desaparece da stack, e o objeto se torna candidato à coleta. Simples, não?
Particularidades do Kotlin/Native: garbage collector sem JVM
\n\nKo