Desempenho Java: de "lentidão" a resposta reativa
Você já iniciou sua aplicação Java e ela "pensou" por um segundo ou dois, como se decidindo se valia a pena atendê-lo? Ou talvez seu servidor começou a sufocar sob carga, e a CPU foi a 100%? Familiar? Bem-vindo ao mundo do desempenho Java — um lugar onde até o código mais elegante pode se transformar em um monstro lento. Mas não se apresse em culpar a linguagem. Na maioria das vezes, o problema não está no Java, mas em como o usamos.
Neste artigo, não vamos ler palestras chatas. Vamos pegar um código real e "lento", executá-lo através de um profiler, encontrar o gargalo e aplicar a otimização Java de modo que a aplicação decole. Pronto? Vamos lá!
Por que as aplicações Java são lentas? (E qual a participação do Garbage Collector)
Antes de pegar as ferramentas, vamos entender os principais inimigos da velocidade:
- Garbage Collector (GC) — amigo ou inimigo? Ele libera memória, mas se você cria milhões de objetos por segundo, o GC trabalhará como um louco, consumindo recursos da CPU. Isso é chamado de GC pauses.
- Estruturas de dados incorretas. Usar
LinkedListonde é necessárioArrayListé como dirigir um tanque em uma pista de corrida. - Bloqueios (Locks). Sincronização é uma faca de dois gumes. Sem ela — caos, com ela — potenciais engarrafamentos.
- Cálculos redundantes. Por que calcular a mesma coisa cem vezes se pode fazer uma vez?
Mas como encontrar o culpado específico? É aqui que entra o profiling Java.
Profiling Java: seu detetive particular
Profiling é como um raio-X para o seu código. Você vê quais métodos são chamados com mais frequência, quanto tempo eles levam e quantos objetos são criados. Sem isso, qualquer otimização é um palpite no escuro.
Ferramentas de profiling: das gratuitas às profissionais
- VisualVM — gratuito e vem com o JDK. Ótimo para análise rápida de CPU e memória.
- JProfiler — poderoso, pago, mas com versão de teste. Fornece relatórios detalhados sobre threads, GC e consultas SQL.
- Java Flight Recorder (JFR) — embutido na HotSpot JVM. Impacto mínimo no desempenho.
- Async Profiler — open-source, opera no nível do kernel, quase não desacelera a aplicação.
Para nosso exemplo, usaremos o VisualVM — é simples e intuitivo.
Exemplo prático: encontrando o problema
Imagine que temos um método que processa uma lista de pedidos e os converte para XML. O código funciona, mas para 10.000 pedidos — é terrivelmente lento. Aqui está ele:
public String generateXmlReport(List<Order> orders) { StringBuilder sb = new StringBuilder(); sb.append("<orders>"); for (Order order : orders) { // Problema: concatenação de strings no loop! sb.append(" <order id=\\"" + order.getId() + "\\">"); sb.append(" <amount>" + order.getAmount() + "</amount>"); sb.append(" </order>"); } sb.append("</orders>"); return sb.toString();}Iniciamos o VisualVM, conectamos à aplicação, clicamos em "CPU Profiler" e disparamos nosso método. Após alguns segundos, vemos o cenário: o método generateXmlReport ocupa 80% do tempo da CPU! E dentro dele — o método StringBuilder.append e… espere… StringBuilder.toString é chamado a cada iteração? Não, é apenas a concatenação com + que cria um novo StringBuilder e String a cada vez. Horror!
Conclusão do profiler: o problema está na concatenação de strings dentro do loop