Perfilando código Kotlin: encontrando gargalos na prática
Quando seu servidor em Kotlin começa a ficar lento e os usuários reclamam de atrasos, a primeira coisa que vem à mente é "provavelmente o problema está no banco de dados". Mas muitas vezes o gargalo está no próprio código: algoritmos lentos, coleções infinitas ou gerenciamento ineficiente de memória. Neste artigo, vamos explorar ferramentas práticas e técnicas de perfilamento de código Kotlin que ajudarão você a encontrar e eliminar gargalos rapidamente.
Por que o perfilamento é importante para o desenvolvedor Kotlin?
Kotlin roda sobre a JVM e, embora nos livre de muitos problemas do Java (como NullPointerException), o desempenho continua sendo um ponto crítico. Mesmo construções simples como map, filter ou forEach podem criar sobrecargas inesperadas se usadas incorretamente. O perfilamento permite olhar dentro da aplicação e entender quais funções consomem mais CPU ou memória.
Neste artigo, abordaremos três abordagens principais:
- Profiler integrado do IntelliJ IDEA (para desenvolvimento local)
- Utilitários de linha de comando
jstackejmap(para produção) - Biblioteca Hiccup (para perfilamento programático)
1. Profiler integrado do IntelliJ IDEA — seu primeiro auxiliar
Se você usa o IntelliJ IDEA Ultimate, já possui uma ferramenta poderosa para perfilamento. Ela funciona diretamente na IDE e não requer instalação de bibliotecas adicionais.
Como executar:
- Inicie sua aplicação no modo de depuração (Debug).
- Vá para a aba Profiler (geralmente no canto inferior direito).
- Clique em CPU Profiling ou Memory Profiling.
- Pare o perfilamento após alguns segundos e analise o Flame Graph (gráfico de chamas).
Exemplo de código para teste:
fun main() { val numbers = (1..1_000_000).toList()
// Gargalo: criação repetida de listas val result = numbers .filter { it % 2 == 0 } .map { it * 2 } .take(100) println(result)}Ao executar o profiler, você verá que .filter() e .map() consomem uma parte significativa do tempo. A solução é usar Sequences (sequências preguiçosas):
fun main() { val numbers = (1..1_000_000).toList()
// Versão otimizada val result = numbers.asSequence() .filter { it % 2 == 0 } .map { it * 2 } .take(100) .toList() println(result)}O profiler mostrará que o tempo de execução reduziu em 2 a 3 vezes.
2. Utilitários de linha de comando: jstack e jmap para produção
Quando a aplicação já está rodando no servidor, você não pode simplesmente conectar o profiler da IDE. É aqui que entram os utilitários do JDK.
jstack — análise de threads (Thread Dump)
Usado quando a aplicação "congela" ou consome 100% de CPU. O jstack mostra quais threads estão sendo executadas no momento.
Exemplo de uso:
# Encontre o PID do seu processo Kotlinps aux | grep kotlin
# Faça um snapshot das threadsjstack -l 12345 > thread_dump.txt
# Analise: procure por bloqueios (Lock) e loops infinitosO que procurar na saída:
- Threads no estado
RUNNABLE— estão usando CPU ativamente. - Threads no estado
BLOCKED— estão aguardando liberação de recurso (geralmente devido a sincronização). - Chamadas repetidas de métodos — sinal de loop infinito.
jmap — análise de memória (Heap Dump)
Se você tem vazamento de memória