必须同时配置-XX:+HeapDumpOnOutOfMemoryError和-XX:HeapDumpPath(绝对路径)才生效,缺一不可;路径需提前创建、确保权限与磁盘空间,并推荐使用%pid%t命名防覆盖,且须配合GC日志定位泄漏节奏。

必须配齐两个JVM参数才能生效
只加 -XX:+HeapDumpOnOutOfMemoryError 不会生成任何文件——它只是个开关,没有路径就等于没按快门。必须同时指定 -XX:HeapDumpPath,且必须是绝对路径。常见错误是写成 -XX:HeapDumpPath=~/dumps(波浪号不展开)或 -XX:HeapDumpPath=./dumps(相对路径在容器或服务化部署中极易失效)。
-
-XX:+HeapDumpOnOutOfMemoryError:启用OOM触发机制,无此参数则完全不响应 -
-XX:HeapDumpPath=/opt/app/dumps/:结尾带斜杠表示目录,JVM会自动生成类似java_pid12345.hprof的文件,避免多实例覆盖 -
-XX:HeapDumpPath=/opt/app/dumps/oom.hprof:指定完整文件名时每次OOM都会覆盖,仅适合单实例调试
路径权限和磁盘空间最容易静默失败
dump 文件生成失败通常不报错、不抛异常、也不打日志,只会安静地“什么都没发生”。根本原因是 JVM 运行用户(如 appuser)对目标路径无写权限,或磁盘已满、inode 耗尽、挂载点不可写(尤其在容器里)。
- 验证方式:
sudo -u appuser touch /opt/app/dumps/test,能写才可信 - 容器场景必须挂载宿主机目录,例如
docker run -v /host/dumps:/opt/app/dumps,不能依赖/tmp(可能被清理或 tmpfs 限制大小) - 推荐路径格式:
/opt/app/dumps/oom_%p_%t.hprof,%p是进程 PID,%t是时间戳,防覆盖且自带上下文
配合 GC 日志才能准确定位泄漏节奏
单有 dump 文件只能看“快照”,但看不出对象何时增长、是否回收失败。没有 GC 日志,你很难判断是内存泄漏(对象持续堆积)还是瞬时峰值(GC 后回落)。
- 建议追加:
-Xloggc:/opt/app/logs/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps - OpenJDK 10+ 推荐用统一日志框架:
-Xlog:gc*:file=/opt/app/logs/gc.log:time,tags,level - 关键要让 GC 日志和 dump 文件时间戳对齐——OOM 前 1–2 分钟的 GC 行为,往往暴露老年代持续增长或 Full GC 频繁失败
生成后必须立刻验证文件有效性
别等分析时才发现 dump 是空的或损坏的。很多线上 dump 文件大小不到 1MB,基本可判定生成失败或截断;真正的堆快照一般几十 MB 起步(取决于堆大小)。
- 检查大小:
ls -lh /opt/app/dumps/*.hprof,小于 5MB 就该警惕 - 确认格式:
file /opt/app/dumps/java_pid*.hprof,正常输出含Java HotSpot字样 - 轻量验证:
jdk/bin/jhsdb jmap --heap --pid $(pgrep -f "your-app.jar")(运行中手动触发一次对比)
java \ -Xms512m -Xmx1024m \ -XX:+HeapDumpOnOutOfMemoryError \ -XX:HeapDumpPath=/opt/app/dumps/oom_%p_%t.hprof \ -Xloggc:/opt/app/logs/gc.log -XX:+PrintGCDetails \ -jar app.jar最常被忽略的一点:
-XX:HeapDumpPath 指定的目录必须提前创建好,且 JVM 启动用户有写权限——JVM 不会自动创建父级目录,也不会提示“目录不存在”。


















