Perfilando código Java: encontrando gargalos na prática

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução: por que perfilar código Java?

\n\n

Desenvolvimento em Java não é apenas escrever código correto, mas também criar aplicações rápidas e escaláveis. Mesmo um sistema perfeitamente projetado pode ficar lento devido a uma única linha não otimizada. Como encontrar esse "gargalo"? A resposta é a perfilagem.

\n\n

Perfilagem é o processo de coleta de dados sobre a operação de uma aplicação: tempo de execução de métodos, uso de memória, threads e carga da CPU. Sem ela, a otimização se torna um palpite. Neste artigo, vamos analisar exemplos práticos de perfilagem de código Java, aprender a interpretar os resultados e encontrar gargalos.

\n\n

Vamos considerar duas ferramentas populares: VisualVM (gratuita, integrada ao JDK) e JProfiler (comercial, mas poderosa). Todos os exemplos serão com código real.

\n\n

Parte principal: prática de perfilagem

\n\n

1. Configuração do ambiente e inicialização do profiler

\n\n

Para começar, certifique-se de ter o JDK instalado (8 ou superior). O VisualVM acompanha o JDK 8 e 9; para versões mais recentes, é necessário baixá-lo separadamente do site oficial.

\n\n

Vamos escrever um código "lento" simples para teste. Crie a classe SlowApp.java:

\n\n
import java.util.ArrayList;\nimport java.util.List;\nimport java.util.Random;\n\npublic class SlowApp {\n    public static void main(String[] args) throws InterruptedException {\n        List<String> data = new ArrayList<>();\n        Random random = new Random();\n\n        // Simulação de carregamento lento de dados\n        for (int i = 0; i < 100_000; i++) {\n            data.add(generateString(random));\n        }\n\n        // Processamento de dados\n        long sum = 0;\n        for (String s : data) {\n            sum += processString(s);\n        }\n\n        System.out.println("Result: " + sum);\n        Thread.sleep(10000); // Para ter tempo de conectar o profiler\n    }\n\n    private static String generateString(Random random) {\n        StringBuilder sb = new StringBuilder(100);\n        for (int i = 0; i < 100; i++) {\n            sb.append((char) ('a' + random.nextInt(26)));\n        }\n        return sb.toString();\n    }\n\n    private static long processString(String s) {\n        long result = 0;\n        for (char c : s.toCharArray()) {\n            result += c;\n            // Atraso artificial\n            for (int j = 0; j < 100; j++) {\n                result += Math.sqrt(j);\n            }\n        }\n        return result;\n    }\n}
\n\n

Execute a aplicação:

\n
javac SlowApp.java\njava SlowApp
\n\n

Enquanto o programa estiver em execução (ele dormirá por 10 segundos), abra o VisualVM. No painel esquerdo, encontre seu processo (SlowApp) e clique duas vezes. Vá para a aba Sampler ou Profiler — você verá estatísticas de CPU e memória.

\n\n

2. Análise de CPU: encontrando o método mais pesado

\n\n

No VisualVM, clique no botão CPU na seção Sampler. Após alguns segundos, você obterá uma tabela com métodos ordenados por tempo de execução (Self Time).

\n\n

Em nosso exemplo, você verá que o método processString ocupa mais de 80% do tempo. Dentro dele, o loop aninhado Math.sqrt(j). Este é o gargalo.

\n\n

Como interpretar isso?

\n
    \n
  • Self Time — tempo gasto diretamente na execução do método (excluindo chamadas de métodos filhos).
  • \n
  • Total Time — tempo total, incluindo todas as chamadas aninhadas.
  • \n
  • Invocations — número de chamadas do método.
  • \n
\n\n

Exemplo de saída (simplificada):

\n
Method                          Self Time (ms)   Invocations\nSlowApp.processString           8500              100000\nSlowApp.generateString          1200              100000\njava.lang.Math.sqrt             6000                

Blogs

Book Recommendations