JDK 8 是唯一能稳定使用 sa-jdi 和 hsdb 直接剖析运行中 JVM 物理内存镜像的主流版本;JDK 9+ 已移除 sa-jdi,jhsdb 功能受限,无法替代其底层内存遍历能力。

sa-jdi 已在 JDK 9+ 中被移除,JDK 8 是唯一能稳定使用 Serviceability Agent(SA)直接剖析正在运行的 JVM 进程物理内存镜像的主流版本;JDK 11/17/21 等后续版本必须改用 jhsdb,且功能受限、不支持所有 SA 原有能力。
确认目标 JVM 是否允许 SA 附加(JDK 8 必须显式解锁)
SA 在 JDK 8u72 及之后默认禁用,即使进程在运行,hsdb 或 jstack -F 也会报 sun.jvm.hotspot.debugger.DebuggerException: Can't attach to the process。
- 启动 JVM 时需至少加上:
-XX:+UnlockDiagnosticVMOptions -XX:+EnableDynamicAgentLoading - 若仍失败,补充
-XX:+StartAttachListener(部分 Linux 发行版内核限制 attach) - 验证是否生效:运行
jinfo -flag +StartAttachListener <pid></pid>,输出应为+StartAttachListener而非-StartAttachListener - 注意:SA 附加会暂停目标 JVM(halted),但不执行任何目标进程内的代码——这是安全前提
用 jstack -F -m 查看实时混合栈(Java + native)
这是最轻量、最常用的 SA 入口,无需图形界面,适合快速定位卡死或崩溃前一刻的状态。
- 命令格式:
jstack -J-d64 -F -m <pid></pid>(-J-d64对 x86_64 必须;-F强制附加;-m显示 C/C++ 栈帧) - 常见错误:
Unable to open socket file→ 检查/tmp/.java_pid<pid></pid>是否存在且可读(权限/SELinux/seccomp 可能拦截) - 若输出中 Java 线程显示
java.lang.Thread.State: RUNNABLE但 native 栈停在pthread_cond_wait或epoll_wait,大概率是线程阻塞在系统调用,不是 JVM 层死锁 - 对比
jstack <pid></pid>(无-F)输出:若后者能正常返回而前者失败,说明 attach 机制被禁用或权限不足
用 hsdb 图形界面直读 JVM 物理内存布局(仅 JDK 8)
hsdb 是 SA 提供的唯一能交互式浏览 JVM 原生内存结构的工具,可查看 C++ HotSpot 对象(如 InstanceKlass、CollectedHeap)、OOP 指针、符号表、GC roots 链路等。
- 启动命令:
java -cp "$JAVA_HOME/lib/sa-jdi.jar" sun.jvm.hotspot.HSDB - 菜单操作:File → Attach to Process… → 输入
pid(不是java进程名) - 关键视图:
-
Inspector:输入地址(如0x00007f8a1c000000)可反解为oop或Klass实例 -
Threads:比jstack多显示线程本地存储(TLS)、JVM 内部线程(如VMThread、CompilerThread) -
Runtime → System Dictionary:查看已加载类的符号引用、instanceKlass 地址、方法计数器
-
- 注意:
hsdb无法连接远程进程,且对容器环境(如 Docker 默认关闭ptrace)天然不友好;docker run --cap-add=SYS_PTRACE是必要条件
jhsdb 在 JDK 11+ 上只能做有限内存快照,不能替代 SA
JDK 9 引入 jhsdb 作为 SA 的“继任者”,但它本质是基于 JVMTI 和调试接口的封装,**无法直接读取进程物理内存镜像**,也不支持 Inspector 类交互式内存遍历。
-
jhsdb jstack --pid <pid></pid>功能接近 JDK 8 的jstack -F,但缺失-m的完整 native 栈解析能力 -
jhsdb clhsdb(command-line HSDB)提供部分命令,如printmem、inspect,但多数命令返回Not yet implemented或Operation not supported - 真正能拿到“物理内存镜像”的方式只剩:
jhsdb jmap --binaryheap --pid <pid></pid>(生成heap.hprof),但这只是堆区快照,不含 Metaspace、CodeCache、线程栈等原生内存 - 结论:若需分析
CodeCache溢出、Metaspace碎片、JNI 全局引用泄漏等底层问题,JDK 8 +hsdb仍是不可替代的方案
JDK 8 的 sa-jdi 和 hsdb 是目前唯一能穿透 JVM 抽象层、直接观察其 C++ 运行时内存结构的组合;所有更高版本都做了主动削弱——这不是遗漏,而是 OpenJDK 社区对调试接口安全性和维护成本的权衡结果。实际排查时,别浪费时间在 JDK 17 上折腾 hsdb,要么降级到 JDK 8 环境复现,要么转向 perf + async-profiler + gdb 的原生栈联合分析路径。


















