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,代码缓存等)的位置和地址范围。
参考文档
- Java 8:无,Java 9才正式引入。
- Java 11:https://docs.oracle.com/en/java/javase/11/tools/jhsdb.html
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文件coredump介绍与开启方式:https://www.cnblogs.com/Anker/p/6079580.html
- options:命令行选项,和子命令有关。
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并不一定能支持。
参考文档:
- http://cr.openjdk.java.net/~minqi/6830717/raw_files/new/agent/doc/clhsdb.html
https://github.com/AdoptOpenJDK/openjdk-jdk11/blob/master/src/jdk.hotspot.agent/doc/clhsdb.html
jhsdb hsdb子命令
无
示例:
# 图形化模式,和clhsdb功能对标
jhsdb hsdb --pid 81033
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和其他工具的对比
功能 | 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 |
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下载地址:
- https://www.oracle.com/java/technologies/javase/jdk11-archive-downloads.html
- 百度加速下载:链接:https://pan.baidu.com/s/1HXjzNDpzin6fXGrZPyQeWQ 密码:aon2 1 代码块
安装完成后,先用JDK 11.0.4启动一个应用,然后即可使用jhsdb工具调试该应用了。