Introduction : pourquoi profiler le code Java ?
\n\nLe développement en Java ne consiste pas seulement à écrire du code correct, mais aussi à créer des applications rapides et évolutives. Même un système parfaitement conçu peut ralentir à cause d'une seule ligne non optimale. Comment trouver ce « goulot d'étranglement » ? La réponse : le profilage.
\n\nLe profilage est le processus de collecte de données sur le fonctionnement d'une application : temps d'exécution des méthodes, utilisation de la mémoire, threads et charge CPU. Sans lui, l'optimisation devient un jeu de devinettes. Dans cet article, nous analyserons des exemples pratiques de profilage de code Java, apprendrons à interpréter les résultats et à trouver les goulots d'étranglement.
\n\nNous examinerons deux outils populaires : VisualVM (gratuit, intégré au JDK) et JProfiler (commercial, mais puissant). Tous les exemples seront accompagnés de code réel.
\n\nPartie principale : pratique du profilage
\n\n1. Configuration de l'environnement et lancement du profileur
\n\nPour commencer, assurez-vous d'avoir installé le JDK (8 ou supérieur). VisualVM est fourni avec JDK 8 et 9 ; pour les versions plus récentes, il doit être téléchargé séparément depuis le site officiel.
\n\nÉcrivons un code « lent » simple pour le test. Créez la classe SlowApp.java :
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 // Simulation d'un chargement long de données\n for (int i = 0; i < 100_000; i++) {\n data.add(generateString(random));\n }\n\n // Traitement des données\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); // Pour avoir le temps de se connecter avec le profileur\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 // Délai artificiel\n for (int j = 0; j < 100; j++) {\n result += Math.sqrt(j);\n }\n }\n return result;\n }\n}\n\nLancez l'application :
\njavac SlowApp.java\njava SlowApp\n\nPendant que le programme s'exécute (il se mettra en veille pendant 10 secondes), ouvrez VisualVM. Dans le panneau de gauche, trouvez votre processus (SlowApp) et double-cliquez. Allez dans l'onglet Sampler ou Profiler — vous verrez les statistiques CPU et mémoire.
\n\n2. Analyse CPU : trouver la méthode la plus lourde
\n\nDans VisualVM, cliquez sur le bouton CPU dans la section Sampler. Après quelques secondes, vous obtiendrez un tableau des méthodes triées par temps d'exécution (Self Time).
\n\nDans notre exemple, vous verrez que la méthode processString occupe plus de 80 % du temps. À l'intérieur, la boucle imbriquée Math.sqrt(j). C'est le goulot d'étranglement.
Comment interpréter cela ?
\n- \n
- Self Time — le temps passé directement sur l'exécution de la méthode (sans tenir compte des appels aux méthodes enfants). \n
- Total Time — le temps total, y compris tous les appels imbriqués. \n
- Invocations — le nombre d'appels à la méthode. \n
Exemple de sortie (simplifié) :
\nMethod Self Time (ms) Invocations\nSlowApp.processString 8500 100000\nSlowApp.generateString 1200 100000\njava.lang.Math.sqrt 6000