Perfilado de código Java: encontrando cuellos de botella en la práctica

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Introducción: ¿por qué perfilar código Java?

\n\n

Desarrollar en Java no solo consiste en escribir código correcto, sino también en crear aplicaciones rápidas y escalables. Incluso un sistema perfectamente diseñado puede ralentizarse debido a una sola línea no óptima. ¿Cómo encontrar ese "cuello de botella"? La respuesta es el perfilado.

\n\n

El perfilado es el proceso de recopilación de datos sobre el funcionamiento de una aplicación: tiempo de ejecución de métodos, uso de memoria, hilos y carga de CPU. Sin él, la optimización se convierte en una adivinanza. En este artículo, analizaremos ejemplos prácticos de perfilado de código Java, aprenderemos a interpretar los resultados y a encontrar cuellos de botella.

\n\n

Veremos dos herramientas populares: VisualVM (gratuita, integrada en JDK) y JProfiler (comercial, pero potente). Todos los ejemplos serán con código real.

\n\n

Parte principal: práctica de perfilado

\n\n

1. Configuración del entorno e inicio del perfilador

\n\n

Para empezar, asegúrese de tener instalado JDK (8 o superior). VisualVM se incluye con JDK 8 y 9; para versiones más recientes, debe descargarse por separado desde el sitio oficial.

\n\n

Escribamos un código "lento" simple para la prueba. Cree la clase 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        // Simulación de carga lenta de datos\n        for (int i = 0; i < 100_000; i++) {\n            data.add(generateString(random));\n        }\n\n        // Procesamiento de datos\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 tener tiempo de conectar el perfilador\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            // Retardo artificial\n            for (int j = 0; j < 100; j++) {\n                result += Math.sqrt(j);\n            }\n        }\n        return result;\n    }\n}
\n\n

Ejecute la aplicación:

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

Mientras el programa se ejecuta (se dormirá durante 10 segundos), abra VisualVM. En el panel izquierdo, encuentre su proceso (SlowApp) y haga doble clic. Vaya a la pestaña Sampler o Profiler — verá estadísticas de CPU y memoria.

\n\n

2. Análisis de CPU: encontrando el método más pesado

\n\n

En VisualVM, presione el botón CPU en la sección Sampler. Después de unos segundos, obtendrá una tabla con los métodos ordenados por tiempo de ejecución (Self Time).

\n\n

En nuestro ejemplo, verá que el método processString ocupa más del 80% del tiempo. Dentro de él, el bucle anidado Math.sqrt(j). Ese es el cuello de botella.

\n\n

¿Cómo interpretar esto?

\n
    \n
  • Self Time — tiempo dedicado directamente a la ejecución del método (sin incluir llamadas a métodos hijos).
  • \n
  • Total Time — tiempo total, incluyendo todas las llamadas anidadas.
  • \n
  • Invocations — número de invocaciones del método.
  • \n
\n\n

Ejemplo de salida (simplificado):

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

Blogs

Book Recommendations