Java ヒープ ダンプのキャプチャと分析
ヒープ ダンプには、実行中の Java アプリケーションが Java ヒープで使用するすべてのライブ オブジェクトのスナップショットが含まれます。 このチュートリアルでは、ヒープ ダンプ、そのさまざまな形式、およびその重要性について説明します。
さらに、OutOfMemoryError
を示す例を見ていきます。これは、ヒープ ダンプをキャプチャするためのさまざまなアプローチとそれを分析するためのツールにつながります。
ヒープ ダンプとその形式の概要
ヒープには、クラスをインスタンス化して作成したすべてのオブジェクトが含まれています。 Java ランタイムのすべてのクラスもこのヒープに作成されます。
このヒープは、JVM (Java 仮想マシン) の開始時に作成され、アプリケーションで破棄または作成されたオブジェクトを調整するために実行時に拡張/縮小できます。
ガベージ コレクション プロセスは、ヒープがいっぱいになるたびに実行されます。このプロセスは、使用されなくなったオブジェクト、または参照されなくなったオブジェクトをすべて収集します (メモリ管理の詳細については こちら を参照してください)。通常、ヒープ ダンプは バイナリ形式の hprof
ファイル。
タイプ、クラス名、アドレス、サイズ、インスタンスに別のオブジェクトへの参照が含まれているかどうかなど、各オブジェクト インスタンスに関する詳細情報を取得できます。 ヒープ ダンプは、次の 2つの形式のいずれかになります。
- ポータブル ヒープ ダンプ形式 (PHD 形式とも呼ばれます)
- クラシックフォーマット
ポータブル ヒープ ダンプはデフォルトの形式であり、さらに分析するために処理する必要があるバイナリであることに注意してください。 一方、古典的な形式は人間が読めるASCIIテキストです。
これら 2つの形式については こちら を参照してください。
ヒープダンプを使用する重要性
通常、アプリケーションが OutOfMemoryError
によってクラッシュした場合、または Java アプリケーションが予想よりも多くのメモリを消費した場合に、ヒープ ダンプの利点が得られます。
ヒープ ダンプは、エラーの主な原因とその他の詳細 (たとえば、すべてのクラスのオブジェクト数、すべてのクラスのメモリ使用量など) を特定するのに役立ちます。
また、アプリケーションの各 Java オブジェクトが占有するメモリ サイズを取得するのにも役立ちます。 このキャプチャされたすべての情報は、メモリ リークの問題を引き起こしている実際のコードを見つけるのに役立ちます。
OutOfMemoryError
を引き起こすコード例を見てみましょう。これは、Java ヒープ ダンプをキャプチャするさまざまな方法につながります。
Java で OutOfMemoryError
を引き起こすコード例
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(String[] args) {
List<byte[]> myList = new ArrayList<>();
int index = 0;
while (true) {
// 1MB each iteration, 1 x 1024 x 1024 = 1048576
byte[] bytes = new byte[1048576];
myList.add(bytes);
Runtime runTime = Runtime.getRuntime();
System.out.printf("[%d] free memory is: %s%n", index++, runTime.freeMemory());
}
}
}
上記のコードは、Java 仮想マシンが十分なメモリを割り当てられない特定のポイントに到達するまで、while
ループを実行してメモリを割り当て続けます。
その時点で、java.lang.OutOfMemoryError: Java heap space
エラーが発生します。
...
...
...
[1510] free memory is: 14687728
[1511] free memory is: 12590576
[1512] free memory is: 10493424
[1513] free memory is: 8396272
[1514] free memory is: 6299120
[1515] free memory is: 4201968
[1516] free memory is: 2104320
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at Test.main(Test.java:16)
ここで、ヒープ ダンプ分析を実行して、OutOfMemoryError
の原因を特定する必要があります。 それは2つのステップで行うことができます。
まず、ヒープ ダンプをキャプチャし、次にヒープ ダンプ ファイルを分析して、疑わしい理由を見つけます。
ヒープ ダンプをキャプチャするさまざまな方法
ヒープ ダンプをキャプチャする方法は複数あります。 以下、一つ一つ学んでいきましょう。
jmap
JVisualVM
jcmd
- ヒープダンプを自動生成する
JMX
jmap
を使用してヒープ ダンプをキャプチャする
jmap
ツールは、実行中の Java 仮想マシン (JVM) のメモリ統計を出力します。 リモートおよびローカルプロセスにも使用できます。
-dump
オプションを使用して、JDK のホーム ディレクトリの bin
フォルダーから使用できる jmap
ツールを使用してヒープ ダンプをキャプチャします。
構文:
jmap -dump:[live],format=b,file=<file-path> <pid>
例:
C:\Program Files\Java\jdk-18\bin> jmap -dump:live,format=b,file=/temp/dump.hprof 12876
以下は、上記で指定したオプションの簡単な説明です。
パラメータ | 説明 |
---|---|
live |
このパラメーターはオプションです。 設定されている場合、アクティブな参照を持つオブジェクトのみが印刷されます。 ガベージ コレクションの準備ができているものは省略されます。 |
format=b |
これは、ダンプ ファイルを保存する形式を指定するために使用されます。 ここでは b を使用しました。これは、このダンプ ファイルがバイナリ形式であることを意味します。 このパラメータが設定されていない場合、結果は同じになります。 |
file |
これは、ダンプが書き込まれる file です。 |
pid |
Java プロセスの ID を示します。 |
pid
を取得するには、jps
コマンドを使用できることに注意してください。 さらに、jmap
は JDK で実験的なツールとして導入されましたが、サポートされていません。 したがって、状況によっては、jmap
を使用する代わりに他のツールを使用する必要がある場合があります。
JVisualVM
を使用してヒープ ダンプをキャプチャする
JVisualVM
は、Java アプリケーションのプロファイリングとトラブルシューティングを追跡できるグラフィカル ユーザー インターフェイスです。 シンプルで使いやすく、ヒープ ダンプを取得できます。
this によると、Oracle JDK 6、7、および 8 で利用可能でした。JDK 9 以降では、JVisualVM
は Oracle JDK では提供されなくなりました。 ユーザーは、使用する場合は個別にダウンロードする必要があります。
visualvm.github.io からダウンロードして、.zip
ファイルを解凍し、bin
フォルダーで visualvm.exe
を見つけてダブルクリックし、プロンプトが表示されたら I Accept
ボタンをクリックします。 次の画面が表示されます。
現在実行中のすべての Java プロセスは、Local
の下に一覧表示されます。 目的の Java プロセスを選択して右クリックし、[ヒープ ダンプ] オプションを選択すると、ヒープ ダンプを取得できます。
必要なすべての情報を示す新しいタブが開きます。
jcmd
を使用してヒープ ダンプをキャプチャする
このツールは、JDK のホーム ディレクトリの bin
フォルダーにもあります。 私たちの場合は、C:\Program Files\Java\jdk-18\bin
です。 デフォルトの場所に Java をインストールしていない場合は、異なる可能性があります。
jcmd
は、コマンド要求を Java 仮想マシンに送信します。 Java プロセスを実行している同じマシンで使用する必要があることに注意してください。
複数のコマンドがあり、そのうちの 1つが GC.heap-dump
で、プロセス ID (pid
) と出力ファイルのパスを指定してヒープ ダンプを取得するために使用します。 以下の jcmd
コマンドの構文を参照してください。
構文:
jcmd <pid> GC.head_dump <file-path>
例:
C:\Program Files\Java\jdk-18\bin> jcmd 12876 GC.head_dump /temp/dump.hprof
jmap
と同様に、ダンプもバイナリ形式で生成されます。
ヒープ ダンプを自動的にキャプチャする
私たちが学んだすべてのアプローチは、特定の時間に手動でヒープ ダンプを取得しますが、状況によっては、java.lang.OutOfMemoryError
が発生するとすぐにヒープ ダンプを生成する必要があります。
ここで、ヒープ ダンプを自動的に生成すると、エラーの調査に役立ちます。
これらのシナリオを考慮して、Java は、アプリケーションが java.lang.OutOfMemoryError
をスローしたときにヒープ ダンプを生成できるコマンド ライン オプションである HeapDumpOnOutOfMemoryError
を提供します。
java -XX:+HeapDumpOnOutOfMemoryError
デフォルトでは、上記のコマンドは、アプリケーションが実行されている場所にある java_pid<pid>.hprof
ファイルにダンプを保存します。 カスタム ディレクトリまたはファイルを指定して、それを HeapDumpPath
オプションで設定できます。
構文:
java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=<file-or-dir-path>
例:
java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/temp/heapdump.bin
これで、このオプションを使用してアプリケーションがメモリ不足になるたびに、次のようにログでヒープ ダンプを含む作成されたファイルを見つけることができます。
java.lang.OutOfMemoryError: Requested array size exceeds VM limit
During heap to java_pid12876.hprof...
Exception in thread "main" Head dump file created [4745371 bytes in 0.028 secs]
java.lang.OutOfMemoryError: Requested array size exceeds VM limit
上記のテキストが java_pid12876.hprof
ファイルに書き込まれていることがわかります。このオプションを使用してアプリケーションを実行している間、オーバーヘッドはありません。
OutOfMemoryError
がいつ発生するかわからないため、すべてのアプリケーション、特に本番環境でこのオプションを使用することをお勧めします。
HotSpotDiagnostic MBean
を使用して実行時にこのオプションを使用できることを思い出してください。 そのために、JConsole
を使用し、HeapDumpOnOutOfMemoryError
VM オプションを true
に設定します。
JMX
を使用してヒープ ダンプをキャプチャする
このメソッドでは、次の 2つのパラメーターを受け入れる dumpHeap
メソッドを提供する HotSpotDiagnostic MBean
を使用します。
パラメータ | 説明 |
---|---|
outputFile |
ダンプの出力ファイルのパスです。 このファイルには、ダンプを保持するために .hprof 拡張子が必要です。 |
live |
true に設定すると、このチュートリアルで jmap を使用して学んだように、メモリ内のアクティブなオブジェクトのみがダンプされます。 |
ヒープ ダンプをキャプチャする、プログラムで呼び出す、または JDK のホーム ディレクトリの bin
フォルダーにある JConsole
のような JMX
クライアントを使用する 2つの方法で呼び出すことができます。 ここでは JMX
を使用しますが、プログラムで呼び出す方法については こちら を参照してください。
JMX
クライアント (JConsole
) 経由で HotSpotDiagnostic MBean
を使用するのが、JConsole
を開き、実行中の Java プロセスに接続し、MBeans
タブに移動し、com の下で
HotSpotDiagnostic を探す最も簡単な方法です。 .sun.management
.
Operations
ドロップダウンの下に dumpHeap
メソッドがあります。ここで、outputFile
および live
パラメータを p0
および p1
テキスト フィールドに指定して、以下に示すように dumpHeap
操作を実行できます。
ここで、Java ヒープ ダンプを分析します。
Java ヒープ ダンプの分析
Java ヒープ ダンプでは、高メモリを使用しているオブジェクト
、メモリを解放していないオブジェクトを見つけるためのオブジェクト グラフ
、および到達可能オブジェクトと到達不能オブジェクト
を探す必要があります。
Eclipse Memory Analyzer (MAT) は、先ほど生成した Java ヒープ ダンプを分析するのに最適です。 これを行うために、Memory Analyzer Tool を開始し、ヒープ ダンプ ファイルを開きます。
Eclipse メモリ アナライザー (MAT) には、以下で簡単に説明する 2 種類のオブジェクト サイズがあります。
浅いヒープ サイズ
- オブジェクトの浅いヒープは、メモリ内のサイズです。Retained Heap Size
- オブジェクトがガベージ コレクションされると解放されるメモリの量です。
ヒープ ダンプ ファイルを開くと、アプリケーションのメモリ使用量の概要を確認できます。 これで、OutOfMemoryError
の原因を簡単に突き止めることができます。