Berbagai Cara untuk Menangkap Heap Dumps Java

1. Perkenalan

Dalam artikel ini, kami akan menunjukkan berbagai cara untuk merekam heap dump di Java.

Heap dump adalah cuplikan dari semua objek yang ada di memori di JVM pada saat tertentu . Mereka sangat berguna untuk memecahkan masalah kebocoran memori dan mengoptimalkan penggunaan memori di aplikasi Java.

Heap dump biasanya disimpan dalam file hprof format biner. Kami dapat membuka dan menganalisis file ini menggunakan alat seperti jhat atau JVisualVM. Selain itu, untuk pengguna Eclipse sangat umum menggunakan MAT.

Di bagian selanjutnya, kita akan membahas beberapa alat dan pendekatan untuk menghasilkan heap dump dan kami akan menunjukkan perbedaan utama di antara keduanya.

2. Alat JDK

JDK dilengkapi dengan beberapa alat untuk merekam heap dump dengan cara yang berbeda. Semua alat ini terletak di bawah folder bin di dalam direktori home JDK . Oleh karena itu, kita dapat memulainya dari baris perintah selama direktori ini termasuk dalam jalur sistem.

Di bagian selanjutnya, kami akan menunjukkan cara menggunakan alat ini untuk menangkap heap dump.

2.1. jmap

jmap adalah alat untuk mencetak statistik tentang memori dalam JVM yang sedang berjalan. Kami dapat menggunakannya untuk proses lokal atau jarak jauh.

Untuk merekam heap dump menggunakan jmap kita perlu menggunakan opsi dump :

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

Bersamaan dengan opsi itu, kita harus menentukan beberapa parameter:

  • live : jika disetel hanya mencetak objek yang memiliki referensi aktif dan membuang objek yang siap untuk sampah dikumpulkan. Parameter ini opsional
  • format = b : menetapkan bahwa file dump akan dalam format biner. Jika tidak diatur hasilnya sama
  • file : file tempat dump akan ditulis
  • pid : id dari proses Java

Contohnya seperti ini:

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

Ingatlah bahwa kita bisa dengan mudah mendapatkan pid dari proses Java dengan menggunakan perintah jps .

Perlu diingat bahwa jmap diperkenalkan di JDK sebagai alat eksperimental dan tidak didukung. Oleh karena itu, dalam beberapa kasus, mungkin lebih baik menggunakan alat lain sebagai gantinya.

2.2. jcmd

jcmd adalah alat yang sangat lengkap yang bekerja dengan mengirimkan permintaan perintah ke JVM. Kami harus menggunakannya di mesin yang sama tempat proses Java sedang berjalan.

Salah satu dari banyak perintahnya adalah GC.heap_dump . Kita bisa menggunakannya untuk mendapatkan heap dump hanya dengan menentukan pid dari proses dan jalur file keluaran:

jcmd  GC.heap_dump 

Kita bisa mengeksekusinya dengan parameter yang sama dengan yang kita gunakan sebelumnya:

jcmd 12587 GC.heap_dump /tmp/dump.hprof

Seperti halnya dengan jmap, dump yang dihasilkan dalam format biner.

2.3. JVisualVM

JVisualVM adalah alat dengan antarmuka pengguna grafis yang memungkinkan kita memantau, memecahkan masalah, dan membuat profil aplikasi Java . GUI-nya sederhana tetapi sangat intuitif dan mudah digunakan.

Salah satu dari banyak opsinya memungkinkan kita untuk merekam heap dump. Jika kita mengklik kanan pada proses Java dan memilih opsi "Heap Dump" , alat tersebut akan membuat heap dump dan membukanya di tab baru:

Perhatikan bahwa kita dapat menemukan jalur dari file yang dibuat di bagian "Info Dasar" .

Mulai dari JDK 9, Visual VM tidak disertakan dalam distribusi Oracle JDK dan Open JDK. Oleh karena itu, jika kita menggunakan Java 9 atau versi yang lebih baru, kita bisa mendapatkan JVisualVM dari situs proyek sumber terbuka Visual VM.

3. Menangkap Heap Dump Secara Otomatis

All the tools that we've shown in the previous sections are intended to capture heap dumps manually at a specific time. In some cases, we want to get a heap dump when a java.lang.OutOfMemoryError occurs so it helps us investigate the error.

For these cases, Java provides the HeapDumpOnOutOfMemoryError command-line option that generates a heap dump when a java.lang.OutOfMemoryError is thrown:

java -XX:+HeapDumpOnOutOfMemoryError

By default, it stores the dump in a java_pid.hprof file in the directory where we're running the application. If we want to specify another file or directory we can set it in the HeapDumpPath option:

java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=

When our application runs out of memory using this option, we'll be able to see in the logs the created file that contains the heap dump:

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)

In the example above, it was written to the java_pid12587.hprof file.

As we can see, this option is very useful and there is no overhead when running an application with this option. Therefore, it's highly recommended to use this option always, especially in production.

Finally, this option can also be specified at runtime by using the HotSpotDiagnostic MBean. To do so, we can use JConsole and set the HeapDumpOnOutOfMemoryError VM option to true:

We can find more information about MBeans and JMX in this 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.

Sebagai aturan praktis, kita harus ingat untuk selalu menggunakan opsi HeapDumpOnOutOfMemoryError saat menjalankan aplikasi Java. Untuk tujuan lain, salah satu alat lainnya dapat digunakan dengan sempurna selama kita mengingat status jmap yang tidak didukung.

Seperti biasa, kode sumber lengkap dari contoh tersedia di GitHub.