作用

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文件,然后启动即可。步骤如下:

# 修改环境变量JAVA_HOME(这里用export临时修改环境变量,当然也可永久修改)
export JAVA_HOME="/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home"

# 为sa-jdi.jar授予执行权限
sudo chmod +x $JAVA_HOME/lib/sa-jdi.jar

# 启动方式1:使用交互式命令行调试器(相当于jhsdb clhsdb)
java -cp $JAVA_HOME/lib/sa-jdi.jar sun.jvm.hotspot.CLHSDB

# 启动方式2:使用交互式GUI调试器启动jhsdb(相当于jhsdb hsdb)
java -cp $JAVA_HOME/lib/sa-jdi.jar sun.jvm.hotspot.HSDB

使用说明

# 启动交互式命令行调试器
jhsdb clhsdb [--pid pid | --exe executable --core coredump]

# 启动远程调试服务器
jhsdb debugd [options] (pid | executable coredump) [server-id]

# 启动交互式GUI调试器
jhsdb hsdb [--pid pid | --exe executable --core coredump]

# 打印堆栈并锁定信息
jhsdb jstack [--pid pid | --exe executable --core coredump] [options]

# 打印堆信息
jhsdb jmap [--pid pid | --exe executable --core coredump] [options]

# 打印基本的JVM信息
jhsdb jinfo [--pid pid | --exe executable --core coredump] [options]

# 打印性能计数器信息
jhsdb jsnap [options] [--pid pid | --exe executable --core coredump] 

其中:

  • pid:进程号
  • server-id:当多个调试服务器在同一远程主机上运行时使用的可选唯一ID
  • executable:从中生成核心转储的Java可执行文件
  • coredump:jhsdb工具连接到的Dump文件
  • options:命令行选项,和子命令有关。

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

options说明

jhsdb clhsdb子命令

示例:

# 进入clhsdb
jhsdb clhsdb --pid 81033

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

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

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

参考文档

jhsdb hsdb子命令

示例:

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

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

jhsdb jinfo子命令

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

示例:

# 打印80904进程的VM标志
jhsdb jinfo --flags --pid 80904

# 打印80904进程的系统属性
jhsdb jinfo --sysprops --pid 80904

jhsdb jmap子命令

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

示例:

# 打印81033进程Java堆的直方图
jhsdb jmap --histo --pid 81033

# 将81033进程的Java堆dump到2.hprof
jmap --binaryheap --dumpfile 2.hprof --pid 81033

jhsdb jstack子命令

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

示例:

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

jhsdb jsnap子命令

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

示例:

jhsdb jsnap --all --pid 81033

相当于:

jcmd 81033 PerfCounter.print

jhsdb debugd子命令

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

示例:

jhsdb debugd 81033

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

jhsdb和其他工具的对比

功能JHSDBJCMD类似工具
展示Java进程N/Ajcmdjps -lm
堆Dumpjhsdb jmap --binaryheapjcmd pid GC.heap_dumpjmap -dump pid
堆使用直方图jhsdb jmap --histojcmd pid GC.class_histogramjmap -histo pid
线程Dumpjhsdb jstack --locks (subset of locked thread frames)jcmd pid Thread.printjstack pid
展示系统属性jhsdb jinfo --syspropsjcmd pid VM.system_propertiesjinfo -sysprops pid
列出VM标记jhsdb jinfo --flagsjcmd pid VM.flagsjinfo -flags pid

macOS下遇到的问题

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

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
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
	at jdk.hotspot.agent/sun.jvm.hotspot.HotSpotAgent.setupVM(HotSpotAgent.java:436)
	at jdk.hotspot.agent/sun.jvm.hotspot.HotSpotAgent.go(HotSpotAgent.java:306)
	at jdk.hotspot.agent/sun.jvm.hotspot.HotSpotAgent.attach(HotSpotAgent.java:141)
	at jdk.hotspot.agent/sun.jvm.hotspot.tools.Tool.start(Tool.java:185)
	at jdk.hotspot.agent/sun.jvm.hotspot.tools.Tool.execute(Tool.java:118)
	at jdk.hotspot.agent/sun.jvm.hotspot.tools.JStack.runWithArgs(JStack.java:90)
	at jdk.hotspot.agent/sun.jvm.hotspot.SALauncher.runJSTACK(SALauncher.java:259)
	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

Q.E.D.

知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议

那一年,我也变成了光!!