Modi diversi per acquisire i dump di heap Java

1. Introduzione

In questo articolo, mostreremo diversi modi per acquisire un dump dell'heap in Java.

Un dump dell'heap è un'istantanea di tutti gli oggetti che si trovano in memoria nella JVM in un determinato momento . Sono molto utili per risolvere i problemi di perdita di memoria e ottimizzare l'utilizzo della memoria nelle applicazioni Java.

I dump di heap vengono solitamente archiviati in file hprof in formato binario. Possiamo aprire e analizzare questi file utilizzando strumenti come jhat o JVisualVM. Inoltre, per gli utenti di Eclipse è molto comune utilizzare MAT.

Nelle sezioni successive, esamineremo più strumenti e approcci per generare un dump dell'heap e mostreremo le principali differenze tra di loro.

2. Strumenti JDK

Il JDK viene fornito con diversi strumenti per acquisire i dump di heap in modi diversi. Tutti questi strumenti si trovano nella cartella bin all'interno della home directory di JDK . Pertanto, possiamo avviarli dalla riga di comando purché questa directory sia inclusa nel percorso di sistema.

Nelle sezioni successive, mostreremo come utilizzare questi strumenti per acquisire i dump di heap.

2.1. jmap

jmap è uno strumento per stampare statistiche sulla memoria in una JVM in esecuzione. Possiamo usarlo per processi locali o remoti.

Per catturare un dump dell'heap usando jmap dobbiamo usare l' opzione dump :

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

Insieme a questa opzione, dovremmo specificare diversi parametri:

  • live : se impostato stampa solo gli oggetti che hanno riferimenti attivi e scarta quelli pronti per essere garbage collection. Questo parametro è facoltativo
  • format = b : specifica che il file dump sarà in formato binario. Se non impostato il risultato è lo stesso
  • file : il file in cui verrà scritto il dump
  • pid : id del processo Java

Un esempio potrebbe essere questo:

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

Ricorda che possiamo facilmente ottenere il pid di un processo Java utilizzando il comando jps .

Tieni presente che jmap è stato introdotto nel JDK come strumento sperimentale e non è supportato. Pertanto, in alcuni casi, potrebbe essere preferibile utilizzare altri strumenti.

2.2. jcmd

jcmd è uno strumento molto completo che funziona inviando richieste di comandi alla JVM. Dobbiamo usarlo nella stessa macchina in cui è in esecuzione il processo Java.

Uno dei suoi numerosi comandi è GC.heap_dump . Possiamo usarlo per ottenere un dump dell'heap semplicemente specificando il pid del processo e il percorso del file di output:

jcmd  GC.heap_dump 

Possiamo eseguirlo con gli stessi parametri che abbiamo usato prima:

jcmd 12587 GC.heap_dump /tmp/dump.hprof

Come con jmap, il dump generato è in formato binario.

2.3. JVisualVM

JVisualVM è uno strumento con un'interfaccia utente grafica che ci consente di monitorare, risolvere i problemi e profilare le applicazioni Java . La GUI è semplice ma molto intuitiva e facile da usare.

Una delle sue numerose opzioni ci consente di acquisire un dump dell'heap. Se facciamo clic con il pulsante destro del mouse su un processo Java e selezioniamo l' opzione "Heap Dump" , lo strumento creerà un heap dump e lo aprirà in una nuova scheda:

Si noti che possiamo trovare il percorso del file creato nella sezione "Informazioni di base" .

A partire da JDK 9, Visual VM non è inclusa nelle distribuzioni Oracle JDK e Open JDK. Pertanto, se stiamo utilizzando Java 9 o versioni più recenti, possiamo ottenere JVisualVM dal sito del progetto open source di Visual VM.

3. Acquisire automaticamente un dump dell'heap

Tutti gli strumenti che abbiamo mostrato nelle sezioni precedenti hanno lo scopo di acquisire manualmente i dump dell'heap in un momento specifico. In alcuni casi, desideriamo ottenere un dump dell'heap quando si verifica un'eccezione java.lang.OutOfMemoryError, in modo che ci aiuti a indagare sull'errore.

Per questi casi, Java fornisce l' opzione della riga di comando HeapDumpOnOutOfMemoryError che genera un dump dell'heap quando viene lanciato un java.lang.OutOfMemoryError :

java -XX:+HeapDumpOnOutOfMemoryError

Per impostazione predefinita, memorizza il dump in un file java_pid.hprof nella directory in cui stiamo eseguendo l'applicazione. Se vogliamo specificare un altro file o directory possiamo impostarlo nell'opzione HeapDumpPath :

java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=

Quando la nostra applicazione esaurisce la memoria utilizzando questa opzione, saremo in grado di vedere nei log il file creato che contiene il dump dell'heap:

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)

Nell'esempio sopra, è stato scritto nel file java_pid12587.hprof .

Come possiamo vedere, questa opzione è molto utile e non vi è alcun sovraccarico quando si esegue un'applicazione con questa opzione. Pertanto, si consiglia vivamente di utilizzare sempre questa opzione, soprattutto in produzione.

Infine, questa opzione può essere specificata anche in fase di esecuzione utilizzando l' MBean HotSpotDiagnostic . Per fare ciò, possiamo usare JConsole e impostare l' opzione HeapDumpOnOutOfMemoryError VM su true :

Possiamo trovare ulteriori informazioni su MBeans e JMX in questo articolo.

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.

Come regola pratica, dovremmo ricordarci di usare sempre l'opzione HeapDumpOnOutOfMemoryError quando si eseguono applicazioni Java. Per altri scopi, qualsiasi altro strumento può essere perfettamente utilizzato purché si tenga presente lo stato non supportato di jmap.

Come sempre, il codice sorgente completo degli esempi è disponibile su GitHub.