Por que o Kotlin às vezes fica lento e como corrigir isso?
\n\nVocê escreveu um código elegante em Kotlin. Ele é conciso, seguro e — de repente — fica lento. Situação familiar? Kotlin é uma linguagem poderosa que roda sobre a JVM, mas seu açúcar sintático e recursos como inline ou sequence podem tanto acelerar quanto desacelerar a aplicação. Neste artigo, vamos analisar como perfilar Kotlin e o que fazer com os resultados para que seu código voe.
Não vamos reescrever guias sobre profilers Java. Em vez disso, focaremos nas especificidades da otimização Kotlin: de lambdas e coleções a corrotinas e funções inline. Vamos lá!
\n\nFerramentas para Perfilamento Kotlin
\n\nAntes de otimizar, é preciso medir. Aqui estão três ferramentas práticas que vale a pena adicionar ao seu arsenal.
\n\n1. IntelliJ Profiler (integrado)
\nA maneira mais simples é executar o profiler diretamente da IDE. Ele mostra CPU, memória e threads. É ótimo para encontrar "pontos quentes" no código Kotlin.
\n\n2. JMH (Java Microbenchmark Harness)
\nPara microbenchmarks. O código Kotlin é compilado para bytecode JVM, então o JMH funciona perfeitamente. Esta é sua escolha se precisar comparar o desempenho de listOf versus sequence.
3. Android Studio Profiler
\nPara aplicações móveis. Mostra vazamentos de memória e frequência do GC. O código Kotlin no Android frequentemente sofre com a criação de objetos desnecessários devido a lambdas.
\n\nImportante: não otimize às cegas. Primeiro, faça o perfilamento, depois corrija. Só assim o desempenho Kotlin se tornará previsível.
\n\n5 exemplos reais de otimização de código Kotlin
\n\nA teoria é boa, mas vamos à prática. Aqui estão cinco cenários típicos onde o Kotlin pode "ficar lento" e maneiras de corrigir isso.
\n\n1. Funções inline: quando o açúcar sintático prejudica
\nKotlin incentiva o estilo funcional. Mas cada lambda é um objeto. Se você usar filter{}.map{} em um loop, dezenas de classes anônimas são criadas.
// Ruim: cria um objeto lambda a cada iteração\nfun processItems(items: List<Int>) {\n items.forEach { item ->\n if (item % 2 == 0) doSomething(item)\n }\n}\n\n// Bom: inline + loop comum\ninline fun processItemsFast(items: List<Int>) {\n for (item in items) {\n if (item % 2 == 0) doSomething(item)\n }\n}\n\nUse inline para funções de ordem superior que são chamadas com frequência. Mas não abuse — código inline muito grande incha o bytecode.
2. Sequências (Sequence) versus List
\nCadeias de filter, map e sorted em coleções criam listas intermediárias. Sequence funciona de forma lazy e economiza memória.
// List: cria 3 listas intermediárias\nval result = list\n .filter { it > 10 }\n .map { it * 2 }\n .take(5)\n\n// Sequence: calculado de forma lazy, uma única passagem\nval resultSeq = list.asSequence()\n .filter { it > 10 }\n .map { it * 2 }\n .take(5)\n .toList()\n\nPara coleções grandes (10k+ elementos), sequence ganha em memória e velocidade. Para coleções pequenas, list é mais rápido devido à sobrecarga do wrapper.
3. Corrotinas: Dispatchers e contexto
\nCorrotinas são o poder do Kotlin, mas um dispatcher incorreto mata o desempenho. Dispatchers.IO para rede, Dispatchers.Default para tarefas de CPU.
// Ruim: bloqueia a thread da UI\nfun loadData() = runBlocking {\n val data = fetchFromNetwork() // bloqueia a thread\n updateUI(data)\n}\n\n// Bom: corrotinas com dispatcher correto\nfun loadDataAsync() = CoroutineScope(Dispatchers.Main).launch {\n val data = withContext(Dispatchers.IO) {\n fetchFromNetwork()\n }\n updateUI(data)\n}\n\nEvite runBlock