1. 序章

このチュートリアルでは、Javaでヒープダンプをキャプチャするさまざまな方法について説明します。

ヒープダンプは、特定の時点でJVMのメモリにあるすべてのオブジェクトのスナップショットです。 これらは、メモリリークの問題をトラブルシューティングし、Javaアプリケーションのメモリ使用量を最適化するのに非常に役立ちます。

ヒープダンプは通常、バイナリ形式のhprofファイルで保存されます。 jhatやJVisualVMなどのツールを使用して、これらのファイルを開いて分析できます。 また、Eclipseユーザーの場合、MATを使用するのが非常に一般的です。

次のセクションでは、ヒープダンプを生成するための複数のツールとアプローチについて説明し、それらの主な違いを示します。

2. JDKツール

JDKには、さまざまな方法でヒープダンプをキャプチャするためのツールがいくつか付属しています。 これらのツールはすべて、JDKホームディレクトリ内のbinフォルダの下にあります。 したがって、このディレクトリがシステムパスに含まれている限り、コマンドラインから起動できます。

次のセクションでは、これらのツールを使用してヒープダンプをキャプチャする方法を見ていきます。

2.1. jmap

jmapは、実行中のJVMのメモリに関する統計を出力するためのツールです。 ローカルまたはリモートプロセスに使用できます。

jmapを使用してヒープダンプをキャプチャするには、dumpオプションを使用する必要があります。

jmap -dump:[live],format=b,file=<file-path> <pid>

そのオプションに加えて、いくつかのパラメーターを指定する必要があります。

  • live :設定されている場合、アクティブな参照を持つオブジェクトのみを出力し、ガベージコレクションの準備ができているオブジェクトを破棄します。 このパラメーターはオプションです。
  • format = b :ダンプファイルがバイナリ形式になることを指定します。 設定されていない場合、結果は同じです。
  • file :ダンプが書き込まれるファイル
  • pid :JavaプロセスのID

例は次のようになります。

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

jps コマンドを使用すると、Javaプロセスのpidを簡単に取得できることを忘れないでください。

また、 jmapは実験ツールとしてJDKに導入されており、サポートされていないことに注意してください。したがって、場合によっては、代わりに他のツールを使用する方が望ましい場合があります。

2.2. jcmd

jcmdは、コマンド要求をJVMに送信することによって機能する非常に完全なツールです。 Javaプロセスが実行されているのと同じマシンで使用する必要があります。

その多くのコマンドの1つは、GC.heap_dumpです。 プロセスのpidと出力ファイルのパスを指定するだけで、ヒープダンプを取得できます。

jcmd <pid> GC.heap_dump <file-path>

以前に使用したのと同じパラメーターで実行できます。

jcmd 12587 GC.heap_dump /tmp/dump.hprof

jmapと同様に、生成されるダンプはバイナリ形式です。

2.3. JVisualVM

JVisualVMは、Javaアプリケーションの監視、トラブルシューティング、およびプロファイリングを可能にするグラフィカルユーザーインターフェイスを備えたツールです。 GUIはシンプルですが、非常に直感的で使いやすいです。

その多くのオプションの1つにより、ヒープダンプをキャプチャできます。 Javaプロセスを右クリックして、「ヒープダンプ」オプションを選択すると、ツールはヒープダンプを作成し、新しいタブで開きます。

「基本情報」セクションで作成されたファイルのパスを見つけることができることに注意してください。

JDK 9以降、VisualVMはOracleJDKおよびOpenJDKディストリビューションに含まれていません。 したがって、Java 9より新しいものを使用している場合は、VisualVMオープンソースプロジェクトサイトからJVisualVMを入手できます。

3. ヒープダンプを自動的にキャプチャする

前のセクションで示したすべてのツールは、特定の時間にヒープダンプを手動でキャプチャすることを目的としています。 場合によっては、 java.lang.OutOfMemoryError が発生したときにヒープダンプを取得して、エラーの調査に役立てることができます。

このような場合、 JavaはHeapDumpOnOutOfMemoryErrorコマンドラインオプションを提供します。これは、java.lang.OutOfMemoryErrorがスローされたときにヒープダンプを生成します。

java -XX:+HeapDumpOnOutOfMemoryError

デフォルトでは、ダンプは java_pid .hprof アプリケーションを実行しているディレクトリ内のファイル。 別のファイルまたはディレクトリを指定する場合は、HeapDumpPathオプションで設定できます。

java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=<file-or-dir-path>

このオプションを使用してアプリケーションのメモリが不足すると、ログにヒープダンプを含む作成済みファイルを見つけることができます。

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)

上記の例では、java_pid12587.hprofファイルに書き込まれています。

ご覧のとおり、このオプションは非常に便利です。 このオプションを使用してアプリケーションを実行する場合、オーバーヘッドはありません。 したがって、特に本番環境では、常にこのオプションを使用することを強くお勧めします。

最後に、このオプションは、HotSpotDiagnosticMBeanを使用して実行時に指定することもできます。 これを行うには、JConsoleを使用して、 HeapDumpOnOutOfMemoryErrorVMオプションをtrueに設定します。

MBeanとJMXの詳細については、この記事を参照してください。

4. JMX

この記事で取り上げる最後のアプローチは、JMXを使用することです。 前のセクションで簡単に紹介したHotSpotDiagnosticMBeanを使用します。 このMBeanは、次の2つのパラメーターを受け入れるdumpHeapメソッドを提供します。

  • outputFile :ダンプ用のファイルのパス。 このファイルの拡張子はhprofである必要があります。
  • live :trueに設定すると、jmapで前に見たように、アクティブなオブジェクトのみがメモリにダンプされます。

次のセクションでは、ヒープダンプをキャプチャするためにこのメソッドを呼び出す2つの異なる方法を示します。

4.1. JConsole

HotSpotDiagnostic MBeanを使用する最も簡単な方法は、JConsoleなどのJMXクライアントを使用することです。

開けたら JConsole 実行中のJavaプロセスに接続し、 [MBeans]タブに移動して、[HotSpotDiagnostic]を下に見つけることができます com.sun.management。 運用では、 dumpHeap 以前に説明した方法:

示されているように、実行するには、パラメータ outputFileおよびlive、p0およびp1テキストフィールドに導入する必要があります。 dumpHeap操作。

4.2. プログラマティックな方法

HotSpotDiagnostic MBeanを使用するもう1つの方法は、Javaコードからプログラムで呼び出すことです。

これを行うには、アプリケーションに登録されているMBeanを取得するために、最初にMBeanServerインスタンスを取得する必要があります。 その後、 HotSpotDiagnosticMXBeanのインスタンスを取得し、そのdumpHeapメソッドを呼び出す必要があります。

コードで見てみましょう:

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);
}

hprofファイルは上書きできないことに注意してください。したがって、ヒープダンプを出力するアプリケーションを作成するときは、これを考慮に入れる必要があります。 そうしないと、例外が発生します。

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. 結論

この記事では、Javaでヒープダンプをキャプチャする複数の方法を学びました。

経験則として、Javaアプリケーションを実行するときは、常にHeapDumpOnOutOfMemoryErrorオプションを使用することを忘れないでください。 jmapのサポートされていないステータスを念頭に置いている限り、さまざまな目的で他のツールを使用できます。

いつものように、例の完全なソースコードは、GitHubから入手できます。