
本文详解线程转储(Thread Dump)与堆转储(Heap Dump)的核心差异,阐明二者在JVM运行时诊断中的不同定位,并结合Spring Boot Actuator的/actuator/threaddump接口,介绍JSON格式线程转储的实用分析方法与推荐工具链。
本文详解线程转储(thread dump)与堆转储(heap dump)的核心差异,阐明二者在jvm运行时诊断中的不同定位,并结合spring boot actuator的`/actuator/threaddump`接口,介绍json格式线程转储的实用分析方法与推荐工具链。
在Java应用性能调优与故障排查中,线程转储(Thread Dump) 和 堆转储(Heap Dump) 是两类互补但目标迥异的关键诊断快照,不可混淆使用。
? 线程转储:聚焦“执行流”状态
线程转储是JVM在某一瞬时对所有Java线程的完整状态快照,包含每个线程的名称、ID、当前状态(如RUNNABLE、BLOCKED、WAITING)、完整调用栈(stack trace),以及锁持有/等待关系。它主要用于诊断高CPU占用、线程阻塞、死锁、线程饥饿等并发行为问题。例如,在Spring Boot Actuator中调用GET /actuator/threaddump(需启用management.endpoints.web.exposure.include=threaddump),将返回结构化JSON格式的线程信息:
{
"threads": [
{
"threadName": "http-nio-8080-exec-1",
"threadState": "RUNNABLE",
"stackTrace": [
{ "className": "com.example.MyService", "methodName": "process", "lineNumber": 42 }
],
"lockedMonitors": [],
"lockedSynchronizers": []
}
]
}? 堆转储:聚焦“内存对象”分布
堆转储则是JVM堆内存的二进制快照(通常为.hprof文件),完整记录了某一时刻所有存活对象的类型、数量、大小、引用链及相互关系。它不关注线程执行逻辑,而是用于定位内存泄漏(Memory Leak)、对象过度创建、大对象驻留等问题。获取方式包括JVM参数(-XX:+HeapDumpOnOutOfMemoryError)、jmap命令或通过VisualVM/MAT触发。
✅ 关键区别总结:
| 维度 | 线程转储(Thread Dump) | 堆转储(Heap Dump) |
|---|---|---|
| 采集目标 | 线程调度与执行状态 | 堆内存中对象实例及其引用关系 |
| 输出格式 | 文本(纯文本或JSON) | 二进制(.hprof) |
| 典型场景 | 死锁、线程卡死、CPU飙升 |
OutOfMemoryError、内存持续增长 |
| 采集开销 | 极低(毫秒级,无停顿) | 较高(可能暂停应用数秒至数十秒) |
? JSON线程转储的高效分析实践
Spring Boot Actuator返回的JSON线程转储虽便于程序解析,但人工阅读仍需结构化支持:
-
IntelliJ IDEA 内置JSON工具:直接打开
.json文件 → 自动语法高亮 + 格式化(Ctrl+Alt+L/Cmd+Option+L)→ 使用结构视图(Structure Tool Window)快速折叠/展开线程块 → 结合搜索(Ctrl+F)定位特定线程名或状态(如"threadState":"BLOCKED"); -
专业分析平台:
- FastThread.io:免费在线上传JSON或文本线程转储,自动生成线程状态统计、锁依赖图、死锁检测报告;
- Thread Dump Analyzer (TDA):开源桌面工具,支持批量对比多个转储,识别线程模式变化;
-
JDK自带
jstack:生成标准文本格式,配合grep/awk脚本快速筛选(如jstack <pid> | grep -A 10 "BLOCKED"</pid>)。
⚠️ 注意事项:
- 单次线程转储价值有限,建议在问题复现时连续采集3–5份间隔5–10秒的转储,观察线程状态演变;
- JSON格式由Actuator序列化生成,字段可能因Spring Boot版本略有差异(如
stackTrace嵌套层级),分析前建议先校验结构; - 堆转储不可用JSON工具分析——必须使用MAT、VisualVM或JProfiler等专用内存分析器加载
.hprof文件。
掌握二者本质差异与适用边界,是构建Java应用可观测性能力的基石:线程转储回答“哪些线程在做什么?为什么卡住了?”,堆转储回答“内存里堆了什么?谁在阻止GC回收?”。在真实故障现场,往往需二者协同——例如先通过线程转储发现某线程长期WAITING于某个锁,再结合堆转储检查该锁对象是否被异常强引用导致无法释放。
立即学习“Java免费学习笔记(深入)”;


















