ヒープダンプは、Java のヒープにあるオブジェクトをすべて書き出したファイル(.hprof)です。 本番では、起動オプションに -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=<ディレクトリ> を入れておき、OutOfMemoryError が起きた瞬間のダンプを残すのが基本です。動いているプロセスからは jcmd <PID> GC.heap_dump <ファイル> で取れます。解析では「どのクラスが増えているか」→「どのオブジェクトがそれを抱えているか(保持サイズ)」→「なぜ回収されないか(GC ルートまでの経路)」の順に絞り込みます。

やりたいこと 方法
OOM の瞬間に自動で取る -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/dumps/
動いているプロセスから取る jcmd <PID> GC.heap_dump /var/dumps/heap.hprof
小さく取る(JDK 17 以降) jcmd <PID> GC.heap_dump -gz=1 /var/dumps/heap.hprof.gz
ダンプを取らずに増えているクラスを見る jcmd <PID> GC.class_histogram を時間をおいて2回
リークの元と経路を軽く記録する JFR の memory-leaks=gc-roots → jfr print --events OldObjectSample
ダンプを開いて調べる Eclipse Memory Analyzer(MAT)・VisualVM

確認環境:OpenJDK 1.8.0_504・17.0.20.1・21.0.10・25.0.4.1(Ubuntu 24.04)で、消えないキャッシュによるメモリリークを再現し、-XX:+HeapDumpOnOutOfMemoryError・jcmd GC.heap_dump・jmap -dump でダンプを取りました。サイズと時間は JDK 21 で、メモリ上のファイルシステム(tmpfs)に書き出して計測しています。解析は jcmd GC.class_histogram、JFR(jfr print)、VisualVM 2.2 に含まれるヒープ解析ライブラリで行いました。Eclipse MAT の画面と一括処理(ParseHeapDump)は Eclipse の公式ドキュメントで確かめています(確認日 2026年10月13日)。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「ヒープダンプの取り方と解析|OutOfMemoryErrorの原因を探す」

ヒープダンプでわかること

わかること わからないこと
クラスごとの個数とサイズ そのオブジェクトがいつ作られたか(JFR で補う)
どのオブジェクトがどれだけのメモリを抱えているか(保持サイズ) 作ったときのスタック(JFR で補う)
GC ルートまでの参照の経路(なぜ回収されないか) ヒープ以外のメモリ(スレッドスタック・ダイレクトバッファの中身の一部・ネイティブ)
フィールドの値(文字列・数値) CPU の使い方

フィールドの値まで入るのが、スレッドダンプとの大きな違いです。これは解析に便利な一方で、個人情報やパスワードがそのまま入ることを意味します(後の章で実際に確かめます)。

OutOfMemoryError のメッセージごとの原因(Java heap space・Metaspace・unable to create native thread など)は、関連記事「Java OutOfMemoryErrorの原因と対処|メッセージ別に解説」で扱っています。この記事は、ヒープダンプの取り方と、ダンプから原因を絞り込む手順に絞ります。

リークを再現するプログラム

ログインのたびに顧客情報をキャッシュに入れ、消さないプログラムです。

import java.util.*;

public class LeakApp {
    static class Customer {
        final String email; final String name; final byte[] avatar;
        Customer(String email, String name, byte[] avatar) { this.email = email; this.name = name; this.avatar = avatar; }
    }
    // 消されないキャッシュ(リークの原因)
    static final Map<String, Customer> SESSION_CACHE = new HashMap<>();

    public static void main(String[] args) throws Exception {
        long delayMs = args.length > 0 ? Long.parseLong(args[0]) : 0;
        int stopAt = args.length > 1 ? Integer.parseInt(args[1]) : Integer.MAX_VALUE;
        for (int i = 0; ; i++) {
            if (i >= stopAt) { Thread.sleep(1000); continue; }
            String id = UUID.randomUUID().toString();
            SESSION_CACHE.put(id, new Customer("user" + i + "@example.com", "山田 太郎" + i, new byte[10_000]));
            if (i % 1000 == 0) System.out.println("cached=" + SESSION_CACHE.size());
            if (delayMs > 0) Thread.sleep(delayMs);
        }
    }
}

static の Map に入れたものは、remove しない限り GC に回収されません。「static なコレクションに入れっぱなし」は、メモリリークでいちばんよく見る形です。

取り方1:OOM の瞬間に自動で取る

java -Xmx64m \
  -XX:+HeapDumpOnOutOfMemoryError \
  -XX:HeapDumpPath=/var/dumps/ \
  -jar app.jar

上のプログラムを -Xmx64m で動かすと、各 JDK で次のように出ました。

java.lang.OutOfMemoryError: Java heap space
Dumping heap to dumps/java_pid10119.hprof ...
Heap dump file created [69561985 bytes in 0.048 secs]
JDK ダンプのサイズ(-Xmx64m) 書き出し時間(ログの値)
8 63,125,582 バイト 0.078 秒
17 68,405,210 バイト 0.077 秒
21 69,561,985 バイト 0.048 秒
25 69,980,361 バイト 0.086 秒

ダンプのサイズは、おおよそ「その時点で使っていたヒープ」の大きさになります。-Xmx8g のアプリなら、数 GB のファイルが出ると見込んでディスクを空けておきます。

HeapDumpPath の書き方と挙動

JDK 21・25 で、HeapDumpPath の書き方を変えて試した結果です。

指定 結果
指定しない 作業ディレクトリに java_pid<PID>.hprof
ディレクトリ(/var/dumps/) /var/dumps/java_pid13389.hprof。毎回別名になる
ファイル名(/var/dumps/heap.hprof) 2回目以降は Unable to create ...: File exists で書かれない
存在しないディレクトリ Unable to create ...: No such file or directory で書かれない
%p を含む(heap-%p.hprof) JDK 25 は heap25-13493.hprof と PID に置き換わった。JDK 17・21 は heap-%p.hprof という名前のまま

ディレクトリを指定し、そのディレクトリを事前に作っておくのが、どの版でも確実です。ファイル名を固定すると、2回目の OOM でダンプが残りません。

一緒に使うオプション

オプション 意味 JDK 21 で確かめた動き
-XX:+ExitOnOutOfMemoryError 最初の OOM で JVM を終了する Terminating due to java.lang.OutOfMemoryError: Java heap space、終了コード 3。ダンプは終了前に書かれた
-XX:+CrashOnOutOfMemoryError 最初の OOM でクラッシュさせ、エラーログ(hs_err)を残す ―
-XX:OnOutOfMemoryError="<コマンド>" OOM 時にコマンドを実行する ―
-XX:HeapDumpGzipLevel=1 OOM 時のダンプを gzip で書く(JDK 17 以降にあるフラグ) ―

コンテナで動かすときは、OOM の後にプロセスが動き続けて応答が乱れるのを避けるため、ExitOnOutOfMemoryError を合わせて使い、再起動を Kubernetes などに任せる構成もあります。ダンプの置き場所は、コンテナが消えても残るボリュームにします。

動いているプロセスに後から設定する

HeapDumpOnOutOfMemoryError と HeapDumpPath は、実行中に変えられるフラグ(manageable)です。再起動せずに設定できました(JDK 21)。

jcmd 13700 VM.set_flag HeapDumpOnOutOfMemoryError true
jcmd 13700 VM.set_flag HeapDumpPath /var/dumps/
jcmd 13700 VM.flags -all | grep -E ' HeapDump(OnOut|Path)'
     bool HeapDumpOnOutOfMemoryError               = true                                   {manageable} {management}
    ccstr HeapDumpPath                             = /var/dumps/                            {manageable} {management}

この後に OOM が起きると、/var/dumps/java_pid13700.hprof が作られました。「次に起きたら取りたい」ときに使えます。

取り方2:jcmd GC.heap_dump(動いているプロセスから)

jcmd 12345 GC.heap_dump /var/dumps/heap.hprof
12345:
Dumping heap to /var/dumps/heap.hprof ...
Heap dump file created [317734329 bytes in 0.474 secs]

ファイル名は JVM プロセスから見たパスです。相対パスにすると JVM の作業ディレクトリに書かれるので、絶対パスで書きます。同じ名前のファイルがあると Unable to create ...: File exists になり、JDK 17 以降は -overwrite で上書きできます。

各版の jcmd <PID> help GC.heap_dump で確かめたオプションです。

オプション JDK 8 JDK 17・21 JDK 25 意味
-all ○ ○ ○ 到達できないオブジェクト(ゴミ)も含める。既定は false(生きているものだけ)
-gz=<1〜9> × ○ ○ gzip で書く。help の説明では 1 が推奨(最も速い)
-overwrite × ○ ○ 同名ファイルを上書きする
-parallel=<数> × × ○ 書き出しに使うスレッド数

取り方3:jmap -dump

jmap -dump:live,format=b,file=/var/dumps/heap.hprof 12345

jmap は既定が all(ゴミも含む)です。 JDK 25 の jmap -h には「all: dump all objects in the heap (default if one of "live" or "all" is not specified)」とあります。jcmd GC.heap_dump は既定が「生きているものだけ」なので、同じつもりで打つと中身が変わります。

項目 jcmd GC.heap_dump jmap -dump
既定の範囲 生きているオブジェクトだけ すべて(live を付けると生きているものだけ)
ゴミも含める -all 既定(または all)
圧縮 -gz=1 gz=1
生きているものだけにするとき 既定 live。先に Full GC が走る

live を付けると、生きているかどうかを確かめるために Full GC が走ります。一方、ゴミを含めるとファイルが大きくなり、解析ツールでの扱いが重くなります。OOM の調査では、生きているオブジェクトだけを見ることが多いです。ゴミは次の GC で回収されるので、OOM の原因にはなりません。

サイズと停止時間を計測

-Xmx1g で約3万件(約 300MB)を抱えた状態のプロセスから、JDK 21 で取り方を変えて計測しました。-Xlog:safepoint で、JVM が全スレッドを止めていた時間(HeapDumper の安全点)も記録しています。

取り方 ファイルサイズ ログの書き出し時間 アプリが止まった時間
jcmd GC.heap_dump 317,734,329 バイト 0.474 秒 473 ms
jcmd GC.heap_dump -all 317,734,329 バイト 0.327 秒 325 ms
jcmd GC.heap_dump -gz=1 8,095,131 バイト 1.153 秒 1,151 ms
jmap -dump:live 317,734,122 バイト 0.533 秒 533 ms
[5.375s][info][safepoint] Safepoint "HeapDumper", Time since last: 4041747237 ns, Reaching safepoint: 4416 ns, Cleanup: 4344 ns, At safepoint: 473004609 ns, Leaving safepoint: 49519 ns, Total: 473062888 ns

読み取れることは次のとおりです。

  • ダンプ中は、アプリの全スレッドが止まります。 今回は書き出し時間とほぼ同じ長さ、止まっていました
  • 書き出し先の速さで止まる時間が変わります。 今回はメモリ上に書いたため速く、ディスクやネットワークのストレージでは長くなります
  • -gz=1 はファイルは小さくなるが、止まる時間は長くなりました(今回のデータは中身がゼロの配列が多いので、特に縮みやすい例です)
  • 今回は捨てられたオブジェクトがほとんどない状態だったため、-all でもサイズは同じでした

数 GB のヒープでは、数秒〜それ以上止まると見込み、ロードバランサーから外してから取る・負荷の低い時間に取るなどの段取りをします。

解析1:クラス別ヒストグラムを2回取って比べる

ヒープダンプを取る前に、まず GC.class_histogram で「何が増えているか」を見ると見当がつきます。ファイルは小さく、すぐ取れます。

jcmd 12222 GC.class_histogram > h1.txt
sleep 10
jcmd 12222 GC.class_histogram > h2.txt
 num     #instances         #bytes  class name (module)
-------------------------------------------------------
   1:         41942       63379816  [B (java.base@21.0.10)
   2:          5295        1045816  [Ljdk.internal.vm.FillerElement; (java.base@21.0.10)
   3:         34796         835104  java.lang.String (java.base@21.0.10)
   ...
   9:          6141         147384  LeakApp$Customer

1回だけだと「byte[] が多い」としかわかりません。2回の差を取ると、増え続けているクラスが浮かびます。 差分を出す短いスクリプトです。

import sys, re

def load(p):
    d = {}
    for line in open(p):
        m = re.match(r'\s*\d+:\s+(\d+)\s+(\d+)\s+(\S+)', line)
        if m:
            d[m.group(3)] = (int(m.group(1)), int(m.group(2)))
    return d

a, b = load(sys.argv[1]), load(sys.argv[2])
rows = [(b[k][1] - a.get(k, (0, 0))[1], b[k][0] - a.get(k, (0, 0))[0], k) for k in b]
print(f"{'増えたバイト':>14} {'増えた個数':>10}  クラス")
for db, dn, k in sorted(rows, reverse=True)[:8]:
    print(f"{db:>14,} {dn:>10,}  {k}")
        増えたバイト      増えた個数  クラス
    47,034,616     18,541  [B
       695,296      3,063  [Ljdk.internal.vm.FillerElement;
       333,792     13,908  java.lang.String
       148,256      4,633  java.util.HashMap$Node
       111,192      4,633  LeakApp$Customer
        24,576          0  [Ljava.util.HashMap$Node;

LeakApp$Customer と HashMap$Node が同じ数(4,633)だけ増えています。 「Customer が HashMap に入り続けている」と読めます。byte[] が約4倍(18,541)増えているのは、1件ごとに avatar の配列と、メールアドレス・名前・UUID の文字列の中身(String は内部に byte[] を持つ)が増えるためです。

GC.class_histogram は既定で生きているオブジェクトだけを数えるため、取るたびに Full GC が走ります。-all を付けるとゴミも数え、GC は走りません。同じことは jmap -histo:live <PID> でもできます。

解析2:JFR の OldObjectSample で「どこで作られ、何が抱えているか」

JFR(JDK Flight Recorder)には、長く生き残っているオブジェクトを抜き取りで記録する jdk.OldObjectSample というイベントがあります。ヒープダンプを取らずに、リークしているオブジェクトの「作られた場所」と「GC ルートまでの経路」がわかります。

# 起動時に記録を始める(JDK 17 以降の書き方)
java -XX:StartFlightRecording:memory-leaks=gc-roots -jar app.jar

# しばらく動かしてから、経路つきで書き出す
jcmd 12222 JFR.dump name=1 path-to-gc-roots=true filename=/var/dumps/leak.jfr

# 読む
jfr print --events OldObjectSample /var/dumps/leak.jfr

memory-leaks は JDK 21 の default.jfc にある設定で、off・types(既定)・stack-traces・gc-roots から選べます。gc-roots にすると、作ったときのスタックトレースも記録されます。15秒動かした記録には jdk.OldObjectSample が60件入っていました。そのうちの1件です。

jdk.OldObjectSample {
  startTime = 14:49:13.903 (2026-10-13)
  duration = 259 ms
  allocationTime = 14:48:59.565 (2026-10-13)
  objectSize = 9.8 kB
  objectAge = 14.3 s
  lastKnownHeapUsage = 3.1 MB
  object =  [
    byte[10000]
    avatar : LeakApp$Customer
    value : java.util.HashMap$Node
    [1676] : java.util.HashMap$Node[16384]
    table : java.util.HashMap Size: 6402
    SESSION_CACHE : java.lang.Class Class Name: LeakApp
  ]
  arrayElements = 10000
  root = {
    description = "Thread Name: main"
    system = "Threads"
    type = "Stack Variable"
  }
  eventThread = "main" (javaThreadId = 1)
  stackTrace = [
    LeakApp.main(String[]) line: 17
  ]
}
項目 読み方
object 下から上に読むと、LeakApp.SESSION_CACHE → HashMap(6,402件)→ … → Customer.avatar → byte[10000] という参照の経路
objectAge 作られてから 14.3 秒、生き残っている
stackTrace 作られた場所。LeakApp.java の17行目(SESSION_CACHE.put(...) の行)
duration 経路を調べるのにかかった時間(259 ms)

ヒープダンプと違い、ファイルは 259.6 kB で、フィールドの値(メールアドレスなど)は入りません。 本番で個人情報を含むダンプを取りにくいときに向いています。JFR の取り方と読み方の全体は、関連記事「JFR(Java Flight Recorder)入門|記録とjfrコマンド分析」で扱っています。

解析3:ヒープダンプを開いて、保持サイズと GC ルートを見る

ヒストグラムと JFR で見当をつけたら、ヒープダンプで確かめます。解析ツールで見る項目は、どのツールでもほぼ同じです。

用語 意味(Eclipse MAT のドキュメントより)
シャロー(shallow)サイズ オブジェクト1個そのものが使うメモリ
保持(retained)サイズ そのオブジェクトが回収されたら、一緒に回収されるオブジェクトの合計
ドミネーターツリー 保持サイズの大きい順に、「誰が誰を抱えているか」を木で表したもの
GC ルートまでの経路 そのオブジェクトが回収されない理由(どこから参照が続いているか)

見るべきは保持サイズです。 HashMap 自体のシャローサイズは 48 バイトですが、保持サイズは中身の全部になります。

VisualVM 2.2 に含まれるヒープ解析ライブラリで、OOM の瞬間のダンプ(JDK 21、69,561,985 バイト)を読んだ結果です。読み込みから経路の計算まで約1.2秒でした。

live: 66,217,704 bytes / 88,480 instances
-- retained size by class (top 5)
     65,077,632       310  java.util.HashMap
     65,069,216       309  java.util.HashMap$Node[]
     64,986,968     7,481  java.util.HashMap$Node
     64,525,824    34,863  byte[]
     64,213,112     6,317  LeakApp$Customer
-- biggest objects by retained size (top 3)
     64,986,080  java.util.HashMap#1
     64,986,032  java.util.HashMap$Node[]#57
        108,456  java.lang.Object[]#564

生きているメモリ 66MB のうち、1つの HashMap(#1)が 65MB を抱えています。 これが「リークの容疑者」です。この HashMap#1 が LeakApp.SESSION_CACHE の値であることも、static フィールドから確かめられました。

-Xmx1g で約3万件を抱えた状態のダンプでは、Customer 1個から GC ルートまでの経路は次のとおりでした。

  LeakApp$Customer (shallow 24, retained 10,160)
  java.util.HashMap$Node (shallow 32, retained 30,832)
  java.util.HashMap$Node[] (shallow 262160, retained 308,654,160)
  java.util.HashMap (shallow 48, retained 308,654,208)
  java.lang.Class (shallow 72, retained 308,654,400)
  java.lang.Class (shallow 72, retained 792)
  GC root kind: sticky class

Customer → HashMap → LeakApp クラス(static フィールド)→ GC ルートとつながっており、クラスが読み込まれている限り回収されないことがわかります。直し方は、キャッシュに上限と期限を付ける(Caffeine などのキャッシュライブラリを使う)か、ログアウト時に remove することです。

Eclipse MAT での手順

Eclipse Memory Analyzer(MAT)は、ヒープダンプの解析で最もよく使われる無償のツールです。公式サイトでは、最新版は 1.17.0(2026年6月10日)で、単体版を動かすには Java 21 以上が必要とされています。ドキュメントに沿った手順は次のとおりです。

手順 MAT での操作 見るもの
1. 開く File > Open Heap Dump... 概要(サイズ・クラス数・オブジェクト数)
2. 容疑者を出す Leak Suspects レポート 大きな塊を抱えるオブジェクトと、その割合
3. クラス別に見る ツールバーのヒストグラム 個数・シャローサイズ・保持サイズ
4. 抱えている人を見る ツールバーのドミネーターツリー 保持サイズの大きいオブジェクトと中身
5. 理由を見る 右クリック > Path to GC Roots 回収されない理由(参照の経路)

大きなダンプは、画面を使わずにサーバー上でレポートだけ作る方法もあります(ドキュメントの例)。

./mat/ParseHeapDump.sh heap.hprof org.eclipse.mat.api:suspects

org.eclipse.mat.api:overview(概要)と org.eclipse.mat.api:top_components(コンポーネント別)も指定できます。レポートは HTML の zip で出るので、ダンプそのものではなくレポートを共有すると、個人情報を広げずに済みます。

JDK 8 まであった jhat は JDK 9 で削除されており(JEP 241)、今の JDK にはヒープダンプを開くツールは付いていません。

ヒープダンプには個人情報がそのまま入る

OOM のダンプ(69,561,985 バイト)を、ただのバイト列として検索しました。

strings java_pid10119.hprof | grep -m3 '@example.com'
H@example.com
user72@example.com!
user204@example.com!
検索した文字列 見つかった数
@example.com(そのままのバイト列) 6,319
山田 太郎(UTF-16LE のバイト列) 6,318

専用ツールを使わなくても、strings だけでメールアドレスが読めました。 日本語は Java の内部表現(UTF-16)で入っているため strings では見えにくいですが、中身はそのまま残っています。セッションの中身・パスワード・API キー・DB から読んだ行など、メモリにあったものはすべて入ると考えます。

対策 内容
権限 作られたファイルは -rw-------(所有者だけ読める)だった。コピーするときもこの権限を保つ
置き場所 本番サーバーの外に出すときは、社内の取り決めに沿った暗号化ストレージに置く
共有 ダンプではなく、MAT のレポートやヒストグラム・JFR の結果を共有する
削除 解析が終わったら消す。保存期間を決めておく
外部サービス オンラインの解析サービスに上げるときは、社外にデータを出すことになる点を確認する
エンドポイント Spring Boot Actuator の /actuator/heapdump は、公開範囲を必ず絞る

Actuator のエンドポイントの公開設定は、関連記事「Spring Boot Actuator設定|エンドポイント一覧と公開設定の雛形」で扱っています。

調査の流れ(まとめ表)

段階 すること 使うもの
備える OOM 時のダンプ出力を設定し、置き場所の空きを確保する HeapDumpOnOutOfMemoryError・HeapDumpPath
見当をつける 増えているクラスを、時間をおいた2回の差で見る GC.class_histogram
作られた場所を知る 長生きしているオブジェクトの作成場所と経路を記録する JFR memory-leaks=gc-roots
確かめる ダンプで保持サイズの大きいオブジェクトと GC ルートを見る MAT・VisualVM
直す 上限・期限のないコレクション、閉じ忘れ、リスナーの登録しっぱなしを直す コード
再発を見る GC 後のヒープ使用量が下がるかを監視する GC ログ・メトリクス

GC ログでヒープの増え方を見る方法は、関連記事「Java ガベージコレクションの仕組みとGC種類比較|ログの読み方まで」で、-Xmx などの起動オプションは関連記事「JVMオプション一覧|Xmx・Xms・GCログ設定をJava 21対応で」で扱っています。

よくある質問

Q. OOM が起きたのにダンプが作られません。 A. 次の順に確かめます。① HeapDumpPath のディレクトリがあるか(無いと No such file or directory)② ファイル名を固定していて、前のファイルが残っていないか(File exists)③ ディスクの空き ④ OOM がヒープ以外(unable to create native thread など)で、そもそもヒープダンプが役に立つ種類か。JVM の標準出力に Dumping heap to ... の行が出ているかも見ます。

Q. ダンプのサイズが -Xmx よりずっと小さいです。 A. ダンプには、その時点で使っていたオブジェクトだけが入ります。-Xmx64m で OOM になったときのダンプは約 63〜70MB でした。-Xmx よりかなり小さいときは、ヒープ全体ではなく、1回で大きな配列を確保しようとして失敗した可能性があります。Red Hat の JBoss EAP のドキュメントでも、ダンプが -Xmx より小さいときは1つのスレッドが大きく確保した可能性を見るよう書かれています。

Q. 本番で取るときに気をつけることは何ですか。 A. ダンプ中はアプリが止まるので、負荷の低い時間に取るか、ロードバランサーから外してから取ります。ディスクの空きはヒープの使用量以上を確保し、取ったファイルは個人情報として扱います。

Q. ダンプを取らずに調べる方法はありますか。 A. jcmd GC.class_histogram を時間をおいて2回取って比べる方法と、JFR の OldObjectSample を使う方法があります。どちらもフィールドの値は入りません。

まとめ

  • 本番では -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=<ディレクトリ> を入れておく。ファイル名を固定すると2回目は書かれない
  • HeapDumpPath の %p は JDK 25 では PID に置き換わったが、JDK 17・21 ではそのままの名前だった
  • 動いているプロセスからは jcmd <PID> GC.heap_dump <絶対パス>。jmap -dump は既定がゴミも含む点が違う
  • ダンプ中はアプリが止まる。約 300MB のダンプで 0.3〜0.5 秒(メモリ上に書いた場合)。-gz=1 はファイルが小さいが止まる時間は長くなった
  • 解析は ヒストグラムの差分 → JFR の OldObjectSample → 保持サイズと GC ルートの順に絞る
  • ヒープダンプにはメールアドレスなどがそのまま入る。権限・置き場所・削除を決めて扱う

参考・出典

確認日はいずれも 2026年10月13日です。

  • JDK 21 Tool Specifications「jcmd」:https://docs.oracle.com/en/java/javase/21/docs/specs/man/jcmd.html
  • JDK 21 Tool Specifications「jmap」:https://docs.oracle.com/en/java/javase/21/docs/specs/man/jmap.html
  • JDK 21 Tool Specifications「java」:https://docs.oracle.com/en/java/javase/21/docs/specs/man/java.html
  • Eclipse Memory Analyzer「Basic Tutorial」:https://help.eclipse.org/latest/topic/org.eclipse.mat.ui.help/gettingstarted/basictutorial.html
  • Eclipse Memory Analyzer「Shallow vs. Retained Heap」:https://help.eclipse.org/latest/topic/org.eclipse.mat.ui.help/concepts/shallowretainedheap.html
  • Eclipse Memory Analyzer「Running Eclipse Memory Analyzer in batch mode」:https://help.eclipse.org/latest/topic/org.eclipse.mat.ui.help/tasks/batch.html
  • Eclipse Memory Analyzer「Download」:https://eclipse.dev/mat/download/
  • Red Hat JBoss EAP 7.4 パフォーマンスチューニングガイド「Java ヒープダンプ」:https://docs.redhat.com/ja/documentation/red_hat_jboss_enterprise_application_platform/7.4/html/performance_tuning_guide/heap_dumps
  • VisualVM(GitHub リリース):https://github.com/oracle/visualvm/releases
  • OpenJDK「JEP 241: Remove the jhat Tool」:https://openjdk.org/jeps/241

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「ヒープダンプの取り方と解析|OutOfMemoryErrorの原因を探す」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?