jhsdb

Jhsdb全称Java Hotspot Debugger,Hotspot进程调试器,可用于从崩溃的JVM附加到Java进程或核心转储。
jhsdb是一款基于Serviceability Agent(可维护性代理,简写为SA)的调试工具。Serviceability Agent是一个JDK组件,用于快照调试、性能分析以及深入了解Hotspot JVM / Hotspot JVM上执行的Java应用程序。
它的工作原理有点类似于Linux上的GDB或者Windows上的Windbg。但尽管诸如gdb的本机调试器可用于检查JVM,但这类本机调试器对Hotspot中的数据结构没有内在了解,因此无法对正在执行的Java应用程序进行深入了解。jhsdb了解JVM关键组件(例如Java堆,堆的代,region,代码缓存等)的位置和地址范围。
参考文档

TIPS
尽管JDK 8及更低版本不直接提供jhsdb命令,但依然其实也是可以使用jhsdb的,只需找到 JAVA_HOME/lib 目录下的sa-jdi.jar文件,然后启动即可。步骤如下:

  1. # 修改环境变量JAVA_HOME(这里用export临时修改环境变量,当然也可永久修改)
  2. export JAVA_HOME="/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home"
  3. # 为sa-jdi.jar授予执行权限
  4. sudo chmod +x $JAVA_HOME/lib/sa-jdi.jar
  5. # 启动方式1:使用交互式命令行调试器(相当于jhsdb clhsdb)
  6. java -cp $JAVA_HOME/lib/sa-jdi.jar sun.jvm.hotspot.CLHSDB
  7. # 启动方式2:使用交互式GUI调试器启动jhsdb(相当于jhsdb hsdb)
  8. java -cp $JAVA_HOME/lib/sa-jdi.jar sun.jvm.hotspot.HSDB

使用说明

  1. # 启动交互式命令行调试器
  2. jhsdb clhsdb [--pid pid | --exe executable --core coredump]
  3. # 启动远程调试服务器
  4. jhsdb debugd [options] (pid | executable coredump) [server-id]
  5. # 启动交互式GUI调试器
  6. jhsdb hsdb [--pid pid | --exe executable --core coredump]
  7. # 打印堆栈并锁定信息
  8. jhsdb jstack [--pid pid | --exe executable --core coredump] [options]
  9. # 打印堆信息
  10. jhsdb jmap [--pid pid | --exe executable --core coredump] [options]
  11. # 打印基本的JVM信息
  12. jhsdb jinfo [--pid pid | --exe executable --core coredump] [options]
  13. # 打印性能计数器信息
  14. jhsdb jsnap [options] [--pid pid | --exe executable --core coredump]

其中:

  • pid:进程号
  • server-id:当多个调试服务器在同一远程主机上运行时使用的可选唯一ID
  • executable:从中生成核心转储的Java可执行文件
  • coredump:jhsdb工具连接到的Dump文件coredump介绍与开启方式:https://www.cnblogs.com/Anker/p/6079580.html
  • options:命令行选项,和子命令有关。

TIPS
–pid、–exe参数二选一必填

options说明

jhsdb clhsdb子命令

示例:

  1. # 进入clhsdb
  2. jhsdb clhsdb --pid 81033

进入交互界面后,输入help,查看相关的命令,命令如下:

  1. Available commands:
  2. assert true | false turn on/off asserts in SA code
  3. attach pid | exec core attach SA to a process or core
  4. buildreplayjars [all | boot | app] build jars for replay, boot.jar for bootclasses, app.jar for application classes
  5. class name find a Java class from debuggee and print oop
  6. classes print all loaded Java classes with Klass*
  7. detach detach SA from current target
  8. dis address [ length ] disassemble (sparc/x86) specified number of instructions from given address
  9. dissemble address disassemble nmethod
  10. dumpcfg -a | id Dump the PhaseCFG for every compiler thread that has one live
  11. dumpclass { address | name } [ directory ] dump .class file for given Klass* or class name
  12. dumpcodecache dump codecache contents
  13. dumpheap [ file ] dump heap in hprof binary format
  14. dumpideal -a | id dump ideal graph like debug flag -XX:+PrintIdeal
  15. dumpilt -a | id dump inline tree for C2 compilation
  16. dumpreplaydata
  17. | -a | [>replay.txt] dump replay data into a file
  18. echo [ true | false ] turn on/off command echo mode
  19. examine [ address/count ] | [ address,address] show contents of memory from given address
  20. field [ type [ name fieldtype isStatic offset address ] ] print info about a field of HotSpot type
  21. findpc address print info. about pointer location
  22. flags [ flag ] show all -XX flag name value pairs. or just show given flag
  23. help [ command ] print help message for all commands or just given command
  24. history show command history. usual !command-number syntax works.
  25. inspect expression inspect a given oop
  26. intConstant [ name [ value ] ] print out hotspot integer constant(s)
  27. jdis address show bytecode disassembly of a given Method*
  28. jhisto show Java heap histogram
  29. jseval script evaluate a given string as JavaScript code
  30. jsload file load and evaluate a JavaScript file
  31. jstack [-v] show Java stack trace of all Java threads. -v is verbose mode
  32. livenmethods show all live nmethods
  33. longConstant [ name [ value ] ] print out hotspot long constant(s)s
  34. mem address [ length ] show contents of memory -- also shows closest ELF/COFF symbol if found
  35. pmap show Solaris pmap-like output
  36. print expression print given Klass*, Method* or arbitrary address
  37. printas type expression print given address as given HotSpot type. eg. print JavaThread <address>
  38. printmdo -a | expression print method data oop
  39. printstatics [ type ] print static fields of given HotSpot type (or all types if none specified)
  40. pstack [-v] show mixed mode stack trace for all Java, non-Java threads. -v is verbose mode
  41. quit quit CLHSDB tool
  42. reattach detach and re-attach SA to current target
  43. revptrs find liveness of oops
  44. scanoops start end [ type ] scan a Oop from given start to end address
  45. search [ heap | codecache | threads ] value search a value in heap or codecache or threads
  46. source filename load and execute CLHSDB commands from given file
  47. symbol name show address of a given ELF/COFF symbol
  48. sysprops show all Java System properties
  49. thread id show thread of id
  50. threads show all Java threads
  51. tokenize ...
  52. type [ type [ name super isOop isInteger isUnsigned size ] ] show info. on HotSpot type
  53. universe print gc universe
  54. vmstructsdump dump hotspot type library in text
  55. verbose true | false turn on/off verbose mode
  56. versioncheck [ true | false ] turn on/off debuggee VM version check
  57. whatis address print info about any arbitrary address
  58. where { -a | id } print Java stack trace of given Java thread or all Java threads (-a)

TIPS
不同版本支持的命令可能不同,这里列出的命令可能你的JVM并不一定能支持。
参考文档:

示例:

  1. # 图形化模式,和clhsdb功能对标
  2. jhsdb hsdb --pid 81033

有关该子命令,详见《jhsdb hsdb》一文。

jhsdb jinfo子命令

  • –flags:打印VM标志
  • –sysprops:打印Java系统属性
  • 留空:打印VM标志和Java系统属性

示例:

  1. # 打印80904进程的VM标志
  2. jhsdb jinfo --flags --pid 80904
  3. # 打印80904进程的系统属性
  4. jhsdb jinfo --sysprops --pid 80904

jhsdb jmap子命令

  • –heap:打印Java堆的概要信息
  • –binaryheap:将Java堆以hprof格式Dump出来
  • –dumpfile:执行dump文件名
  • –histo:打印Java堆的直方图
  • –clstats:打印Java堆的类加载器统计信息
  • –finalizerinfo:打印等待finalization的对象的信息

示例:

  1. # 打印81033进程Java堆的直方图
  2. jhsdb jmap --histo --pid 81033
  3. # 将81033进程的Java堆dump到2.hprof
  4. jmap --binaryheap --dumpfile 2.hprof --pid 81033

jhsdb jstack子命令

  • –locks:打印java.util.concurrent锁的信息
  • –mixed:尝试打印Java栈与本地方法栈的信息(需操作系统支持)

示例:

  1. # 打印81033锁的信息,并尝试打印Java栈与本地方法栈的信息
  2. jhsdb jstack --locks --mixed --pid 81033

jhsdb jsnap子命令

  • –all:打印所有性能计数器的信息

示例:

  1. jhsdb jsnap --all --pid 81033

相当于:

  1. jcmd 81033 PerfCounter.print

jhsdb debugd子命令

  • server-id:当多个调试服务器在同一远程主机上运行时使用的可选唯一ID

示例:

  1. jhsdb debugd 81033

TIPS
debugd子命令的格式和其他子命令不同,这是个bug,在JDK 13中已经和其他子命令保持一致了。
https://bugs.openjdk.java.net/browse/JDK-8223666

jhsdb和其他工具的对比

功能 JHSDB JCMD 类似工具
展示Java进程 N/A jcmd jps -lm
堆Dump jhsdb jmap —binaryheap jcmd pid GC.heap_dump jmap -dump pid
堆使用直方图 jhsdb jmap —histo jcmd pid GC.class_histogram jmap -histo pid
线程Dump jhsdb jstack —locks (subset of locked thread frames) jcmd pid Thread.print jstack pid
展示系统属性 jhsdb jinfo —sysprops jcmd pid VM.system_properties jinfo -sysprops pid
列出VM标记 jhsdb jinfo —flags jcmd pid VM.flags jinfo -flags pid

j

macOS下遇到的问题

在macOS下,目前最新的11.0.7中的jhsdb无法正常使用,会报类似如下异常:

  1. Error attaching to process: sun.jvm.hotspot.runtime.VMVersionMismatchException: Supported versions are 11.0.7+8-LTS. Target VM is 11.0.4+10-LTS
  2. sun.jvm.hotspot.debugger.DebuggerException: sun.jvm.hotspot.runtime.VMVersionMismatchException: Supported versions are 11.0.7+8-LTS. Target VM is 11.0.4+10-LTS
  3. at jdk.hotspot.agent/sun.jvm.hotspot.HotSpotAgent.setupVM(HotSpotAgent.java:436)
  4. at jdk.hotspot.agent/sun.jvm.hotspot.HotSpotAgent.go(HotSpotAgent.java:306)
  5. at jdk.hotspot.agent/sun.jvm.hotspot.HotSpotAgent.attach(HotSpotAgent.java:141)
  6. at jdk.hotspot.agent/sun.jvm.hotspot.tools.Tool.start(Tool.java:185)
  7. at jdk.hotspot.agent/sun.jvm.hotspot.tools.Tool.execute(Tool.java:118)
  8. at jdk.hotspot.agent/sun.jvm.hotspot.tools.JStack.runWithArgs(JStack.java:90)
  9. at jdk.hotspot.agent/sun.jvm.hotspot.SALauncher.runJSTACK(SALauncher.java:259)
  10. at jdk.hotspot.agent/sun.jvm.hotspot.SALauncher.main(SALauncher.java:450)

这是JDK版本的问题(JDK bug,只在macOS下出现),只需将JDK降级至11.0.4即可,也可在Linux或Windows下测试jhsdb工具。
JDK 11.0.4下载地址:

安装完成后,先用JDK 11.0.4启动一个应用,然后即可使用jhsdb工具调试该应用了。

参考文档

https://dzone.com/articles/jhsdb-a-new-tool-for-jdk-9