Différentes façons de capturer des vidages de tas Java

1. Introduction

Dans cet article, nous montrerons différentes manières de capturer un vidage de tas en Java.

Un vidage de tas est un instantané de tous les objets qui sont en mémoire dans la JVM à un moment donné . Ils sont très utiles pour résoudre les problèmes de fuite de mémoire et optimiser l'utilisation de la mémoire dans les applications Java.

Les vidages de tas sont généralement stockés dans des fichiers hprof au format binaire. Nous pouvons ouvrir et analyser ces fichiers à l'aide d'outils tels que jhat ou JVisualVM. De plus, pour les utilisateurs d'Eclipse, il est très courant d'utiliser MAT.

Dans les sections suivantes, nous passerons en revue plusieurs outils et approches pour générer un vidage de tas et nous montrerons les principales différences entre eux.

2. Outils JDK

Le JDK est livré avec plusieurs outils pour capturer les vidages de tas de différentes manières. Tous ces outils se trouvent sous le dossier bin dans le répertoire de base JDK . Par conséquent, nous pouvons les démarrer à partir de la ligne de commande tant que ce répertoire est inclus dans le chemin du système.

Dans les sections suivantes, nous montrerons comment utiliser ces outils afin de capturer des vidages de tas.

2.1. jmap

jmap est un outil pour imprimer des statistiques sur la mémoire dans une JVM en cours d'exécution. Nous pouvons l'utiliser pour des processus locaux ou distants.

Pour capturer un vidage de tas en utilisant jmap, nous devons utiliser l' option dump :

jmap -dump:[live],format=b,file= 

Parallèlement à cette option, nous devons spécifier plusieurs paramètres:

  • live : si défini, il n'imprime que les objets qui ont des références actives et rejette ceux qui sont prêts à être récupérés. Ce paramètre est facultatif
  • format = b : spécifie que le fichier de vidage sera au format binaire. Sinon, le résultat est le même
  • file : le fichier dans lequel le vidage sera écrit
  • pid : id du processus Java

Un exemple serait comme ceci:

jmap -dump:live,format=b,file=/tmp/dump.hprof 12587

N'oubliez pas que nous pouvons facilement obtenir le pid d'un processus Java en utilisant la commande jps .

Gardez à l'esprit que jmap a été introduit dans le JDK en tant qu'outil expérimental et qu'il n'est pas pris en charge. Par conséquent, dans certains cas, il peut être préférable d'utiliser d'autres outils à la place.

2.2. jcmd

jcmd est un outil très complet qui fonctionne en envoyant des requêtes de commande à la JVM. Nous devons l'utiliser dans la même machine où le processus Java est en cours d'exécution.

L'une de ses nombreuses commandes est GC.heap_dump . Nous pouvons l'utiliser pour obtenir un vidage de tas simplement en spécifiant le pid du processus et le chemin du fichier de sortie:

jcmd  GC.heap_dump 

Nous pouvons l'exécuter avec les mêmes paramètres que nous utilisions auparavant:

jcmd 12587 GC.heap_dump /tmp/dump.hprof

Comme avec jmap, le vidage généré est au format binaire.

2.3. JVisualVM

JVisualVM est un outil avec une interface utilisateur graphique qui nous permet de surveiller, dépanner et profiler les applications Java . L'interface graphique est simple mais très intuitive et facile à utiliser.

L'une de ses nombreuses options nous permet de capturer un vidage de tas. Si nous cliquons avec le bouton droit sur un processus Java et sélectionnons l' option «Heap Dump» , l'outil créera un vidage de tas et l'ouvrira dans un nouvel onglet:

Notez que nous pouvons trouver le chemin du fichier créé dans la section «Informations de base» .

À partir du JDK 9, Visual VM n'est pas inclus dans les distributions Oracle JDK et Open JDK. Par conséquent, si nous utilisons Java 9 ou des versions plus récentes, nous pouvons obtenir JVisualVM à partir du site de projet open source Visual VM.

3. Capturez automatiquement un vidage de tas

Tous les outils que nous avons présentés dans les sections précédentes sont destinés à capturer manuellement les vidages de tas à un moment précis. Dans certains cas, nous voulons obtenir un vidage du tas lorsqu'une erreur java.lang.OutOfMemoryError se produit afin de nous aider à enquêter sur l'erreur.

Dans ces cas, Java fournit l' option de ligne de commande HeapDumpOnOutOfMemoryError qui génère un vidage de tas lorsqu'un java.lang.OutOfMemoryError est levé:

java -XX:+HeapDumpOnOutOfMemoryError

Par défaut, il stocke le vidage dans un fichier java_pid.hprof dans le répertoire où nous exécutons l'application. Si nous voulons spécifier un autre fichier ou répertoire, nous pouvons le définir dans l' option HeapDumpPath :

java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=

Lorsque notre application manque de mémoire en utilisant cette option, nous pourrons voir dans les journaux le fichier créé qui contient le vidage du tas:

java.lang.OutOfMemoryError: Requested array size exceeds VM limit Dumping heap to java_pid12587.hprof ... Exception in thread "main" Heap dump file created [4744371 bytes in 0.029 secs] java.lang.OutOfMemoryError: Requested array size exceeds VM limit at com.baeldung.heapdump.App.main(App.java:7)

Dans l'exemple ci-dessus, il a été écrit dans le fichier java_pid12587.hprof .

Comme nous pouvons le voir, cette option est très utile et il n'y a pas de surcharge lors de l'exécution d'une application avec cette option. Par conséquent, il est fortement recommandé de toujours utiliser cette option, en particulier en production.

Enfin, cette option peut également être spécifiée au moment de l'exécution à l'aide du HotSpotDiagnostic MBean . Pour ce faire, nous pouvons utiliser JConsole et définir l' option HeapDumpOnOutOfMemoryError VM sur true :

Nous pouvons trouver plus d'informations sur les MBeans et JMX dans cet article.

4. JMX

The last approach that we'll cover in this article is using JMX. We'll use the HotSpotDiagnostic MBean that we briefly introduced in the previous section. This MBean provides a dumpHeap method that accepts 2 parameters:

  • outputFile: the path of the file for the dump. The file should have the hprof extension
  • live: if set to true it dumps only the active objects in memory, as we've seen with jmap before

In the next sections, we'll show 2 different ways to invoke this method in order to capture a heap dump.

4.1. JConsole

The easiest way to use the HotSpotDiagnostic MBean is by using a JMX client such as JConsole.

If we open JConsole and connect to a running Java process, we can navigate to the MBeans tab and find the HotSpotDiagnostic under com.sun.management. In operations, we can find the dumpHeap method that we've described before:

As shown, we just need to introduce the parameters outputFile and live into the p0 and p1 text fields in order to perform the dumpHeap operation.

4.2. Programmatic Way

The other way to use the HotSpotDiagnostic MBean is by invoking it programmatically from Java code.

To do so, we first need to get an MBeanServer instance in order to get an MBean that is registered in the application. After that, we simply need to get an instance of a HotSpotDiagnosticMXBean and call its dumpHeap method.

Let's see it in code:

public static void dumpHeap(String filePath, boolean live) throws IOException { MBeanServer server = ManagementFactory.getPlatformMBeanServer(); HotSpotDiagnosticMXBean mxBean = ManagementFactory.newPlatformMXBeanProxy( server, "com.sun.management:type=HotSpotDiagnostic", HotSpotDiagnosticMXBean.class); mxBean.dumpHeap(filePath, live); }

Notice that an hprof file cannot be overwritten. Therefore, we should take this into account when creating an application that prints heap dumps. If we fail to do so we'll get an exception:

Exception in thread "main" java.io.IOException: File exists at sun.management.HotSpotDiagnostic.dumpHeap0(Native Method) at sun.management.HotSpotDiagnostic.dumpHeap(HotSpotDiagnostic.java:60)

5. Conclusion

In this tutorial, we've shown multiple ways to capture a heap dump in Java.

En règle générale, nous devons nous rappeler d'utiliser toujours l'option HeapDumpOnOutOfMemoryError lors de l'exécution d'applications Java. À d'autres fins, n'importe lequel des autres outils peut être parfaitement utilisé tant que nous gardons à l'esprit le statut non pris en charge de jmap.

Comme toujours, le code source complet des exemples est disponible à l'adresse over sur GitHub.