Un guide pour async-profiler

1. Vue d'ensemble

Les profileurs d'échantillonnage Java sont généralement conçus à l'aide de JVM Tool Interface (JVMTI) et collectent les traces de pile à un point de restauration. Par conséquent, ces profileurs d'échantillonnage peuvent souffrir du problème de biais de point de sécurité.

Pour une vue holistique de l'application, nous avons besoin d'un profileur d'échantillonnage qui ne nécessite pas que les threads soient aux points de restauration et qui puisse collecter les traces de pile à tout moment pour éviter le problème de biais de point de restauration .

Dans ce didacticiel, nous explorerons async-profiler ainsi que diverses techniques de profilage qu'il propose.

2. async-profiler

async-profiler est un profileur d'échantillonnage pour tout JDK basé sur la JVM HotSpot. Il a une faible surcharge et ne repose pas sur JVMTI.

Il évite le problème de biais safepoint en utilisant l' API AsyncGetCallTrace fournie par HotSpot JVM pour profiler les chemins de code Java, et perf_events de Linux pour profiler les chemins de code natifs.

En d'autres termes, le profileur fait correspondre les piles d'appels du code Java et des chemins de code natif pour produire des résultats précis.

3. Configuration

3.1. Installation

Tout d'abord, nous allons télécharger la dernière version d' Async-profiler basée sur notre plateforme. Actuellement, il ne prend en charge que les plates-formes Linux et macOS.

Une fois téléchargé, nous pouvons vérifier s'il fonctionne sur notre plateforme:

$ ./profiler.sh --version
Async-profiler 1.7.1 built on May 14 2020 Copyright 2016-2020 Andrei Pangin

C'est toujours une bonne idée de vérifier au préalable toutes les options disponibles avec async-profiler :

$ ./profiler.sh
Usage: ./profiler.sh [action] [options] Actions: start start profiling and return immediately resume resume profiling without resetting collected data stop stop profiling check check if the specified profiling event is available status print profiling status list list profiling events supported by the target JVM collect collect profile for the specified period of time and then stop (default action) Options: -e event profiling event: cpu|alloc|lock|cache-misses etc. -d duration run profiling for seconds -f filename dump output to -i interval sampling interval in nanoseconds -j jstackdepth maximum Java stack depth -b bufsize frame buffer size -t profile different threads separately -s simple class names instead of FQN -g print method signatures -a annotate Java method names -o fmt output format: summary|traces|flat|collapsed|svg|tree|jfr -I include output only stack traces containing the specified pattern -X exclude exclude stack traces with the specified pattern -v, --version display version string --title string SVG title --width px SVG width --height px SVG frame height --minwidth px skip frames smaller than px --reverse generate stack-reversed FlameGraph / Call tree --all-kernel only include kernel-mode events --all-user only include user-mode events --cstack mode how to traverse C stack: fp|lbr|no is a numeric process ID of the target JVM or 'jps' keyword to find running JVM automatically

La plupart des options présentées seront utiles dans les sections suivantes.

3.2. Configuration du noyau

Lorsque vous utilisez async-profiler sur la plate-forme Linux, nous devons nous assurer de configurer notre noyau pour capturer les piles d'appels en utilisant perf_events par tous les utilisateurs:

Tout d'abord, nous allons définir perf_event_paranoid sur 1, ce qui permettra au profileur de collecter des informations sur les performances:

$ sudo sh -c 'echo 1 >/proc/sys/kernel/perf_event_paranoid'

Ensuite, nous définirons kptr_restrict sur 0 pour supprimer les restrictions sur l'exposition des adresses du noyau:

$ sudo sh -c 'echo 0 >/proc/sys/kernel/kptr_restrict'

Cependant, le profileur asynchrone fonctionnera de lui-même sur la plate-forme macOS.

Maintenant que notre plateforme est prête, nous pouvons créer notre application de profilage et l'exécuter à l'aide de la commande Java:

$ java -XX:+UnlockDiagnosticVMOptions -XX:+DebugNonSafepoints -jar path-to-jar-file

Ici, nous avons commencé notre application de profilage en utilisant les indicateurs JVM -XX: + UnlockDiagnosticVMOptions -XX: + DebugNonSafepoints JVM qui sont fortement recommandés pour des résultats précis .

Maintenant que nous sommes prêts à profiler notre application, explorons différents types de profilage pris en charge par le profileur async .

4. Profilage du processeur

Async-profiler collecte des exemples de traces de pile de méthodes Java, y compris le code JVM, la classe native et les fonctions du noyau, lors du profilage du processeur.

Profilons notre application en utilisant son PID:

$ ./profiler.sh -e cpu -d 30 -o summary 66959 Started [cpu] profiling --- Execution profile --- Total samples : 28 Frame buffer usage : 0.069%

Ici, nous avons défini l' événement de profilage du processeur en utilisant l' option -e . Ensuite, nous avons utilisé le -d possibilité de prélever l'échantillon pendant 30 secondes.

Enfin, l' option -o est utile pour définir le format de sortie comme résumé, HTML, traces, SVG et arborescence .

Créons la sortie HTML lors du profilage du processeur de notre application:

$ ./profiler.sh -e cpu -d 30 -f cpu_profile.html 66959

Ici, nous pouvons voir que la sortie HTML nous permet de développer, réduire et rechercher les exemples.

De plus, async-profiler prend en charge les graphiques de flamme prêts à l'emploi .

Générons un graphique de flamme en utilisant l' extension de fichier .svg pour le profil CPU de notre application:

$ ./profiler.sh -e cpu -d 30 -f cpu_profile.svg 66959

Ici, le graphique de flamme résultant montre les chemins de code Java en vert, C ++ en jaune et les chemins de code système en rouge.

5. Profil d'allocation

De même, nous pouvons collecter des échantillons d'allocation de mémoire sans utiliser une technique intrusive comme l'instrumentation bytecode.

async-profiler uses the TLAB (Thread Local Allocation Buffer) based sampling technique to collect the samples of the heap allocation above the average size of TLAB.

By using the alloc event, we can enable the profiler to collect heap allocations of our profiling application:

$ ./profiler.sh -e alloc -d 30 -f alloc_profile.svg 66255

Here, we can see the object cloning has allocated a large part of memory, which is otherwise hard to perceive when looking at the code.

6. Wall-Clock Profiling

Also, async-profiler can sample all threads irrespective of their status – like running, sleeping, or blocked – by using the wall-clock profile.

This can prove handy when troubleshooting issues in the application start-up time.

By defining the wall event, we can configure the profiler to collect samples of all threads:

$ ./profiler.sh -e wall -t -d 30 -f wall_clock_profile.svg 66959

Here, we've used the wall-clock profiler in per-thread mode by using the -t option, which is highly recommended when profiling all threads.

Additionally, we can check all profiling events supported by our JVM by using the list option:

$ ./profiler.sh list 66959
Basic events: cpu alloc lock wall itimer Java method calls: ClassName.methodName

7. async-profiler With IntelliJ IDEA

IntelliJ IDEA features integration with async-profiler as a profiling tool for Java.

7.1. Profiler Configurations

We can configure async-profiler in IntelliJ IDEA by selecting the Java Profiler menu option at Settings/Preferences > Build, Execution, Deployment:

Also, for quick usage, we can choose any predefined configuration, like the CPU Profiler and the Allocation Profiler that IntelliJ IDEA offers.

Similarly, we can copy a profiler template and edit the Agent options for specific use cases.

7.2. Profile Application Using IntelliJ IDEA

There are a few ways to analyze our application with a profiler.

For instance, we can select the application and choose Run with option:

Or, we can click on the toolbar and choose the Run with option:

Or, by choosing the Run with Profiler option under the Run menu, then selecting the <profiler configuration name>:

Additionally, we can see the option to Attach Profiler to Process under the Run menu. It opens a dialog that lets us choose the process to attach:

Once our application is profiled, we can analyze the profiling result using the Profiler tool window bar at the bottom of the IDE.

The profiling result of our application will look like:

It shows the thread wise results in different output formats like flame graphs, call trees, and method list.

Alternativement, nous pouvons choisir l' option Profiler dans le menu Affichage> Fenêtres d'outils pour voir les résultats:

8. Conclusion

Dans cet article, nous avons exploré le profileur asynchrone , ainsi que quelques techniques de profilage.

Tout d'abord, nous avons vu comment configurer le noyau lors de l'utilisation de la plate-forme Linux, et quelques indicateurs JVM recommandés pour commencer à profiler notre application afin d'obtenir des résultats précis.

Ensuite, nous avons examiné divers types de techniques de profilage comme le processeur, l'allocation et l'horloge murale.

Enfin, nous avons profilé une application avec async-profiler en utilisant IntelliJ IDEA.