转自https://www.jianshu.com/p/7c40274441a4
最近在网上看到rednaxelafx关于HSDB的介绍,感觉打开了解jvm细节的一扇大门,之前只是纯粹的了解理论, 而现在可以通过该工具去深入查看内部的细节;
概述
SA包含在$JAVA_HOME/lib/sa-jdi.jar中,包括三个工具:
1.CLHSDB:
命令行版本
java -cp .:$JAVA_HOME/lib/sa-jdi.jar sun.jvm.hotspot.CLHSDB
2.HSDB
图形界面版本
java -cp .:$JAVA_HOME/lib/sa-jdi.jar sun.jvm.hotspot.HSDB
3.JSDB:
Javascript引擎版本
java -cp .:$JAVA_HOME/lib/sa-jdi.jar sun.jvm.hotspot.tools.soql.JSDB
用途
SA的一个限制是它只实现了调试snapshot的功能,因此要么要让被调试的目标进程完全暂停,要么就调试core dump;另外注意,SA调试通常要求拥有root权限,否则会报错;
调试本地进程
获取要调试的进程ID:
ps -ef|grep javajps -mvl
attch进程
java -cp .:$JAVA_HOME/lib/sa-jdi.jar sun.jvm.hotspot.CLHSDBattach PID
或
sudo java -cp .:$JAVA_HOME/lib/sa-jdi.jar sun.jvm.hotspot.HSDB通过菜单"File"->"Attach to HotSpot process",录入PID
调试远程进程
远程机器上启动rmiregistry服务;
rmiregistry -J-d64 -J-Xbootclasspath/p:${JAVA_HOME}/lib/sa-jdi.jar 或rmiregistry -J-Xbootclasspath/p:${JAVA_HOME}/lib/sa-jdi.jar
启动debug server:
客户端通过机器名称连接到debug server进行调试,默认情况下,当只启动一个debug server时,机器名称为ip地址;
```bash java -d64 -classpath ${JAVA_HOME}/lib/sa-jdi.jar sun.jvm.hotspot.jdi.SADebugServer 或 java -d64 -classpath ${JAVA_HOME}/lib/sa-jdi.jar sun.jvm.hotspot.jdi.SADebugServer ```
调试core dump
java -cp .:$JAVA_HOME/lib/sa-jdi.jar sun.jvm.hotspot.CLHSDB $JAVA_HOME/bin/java core-file-name
注意:为了使Java程序能够产生core dump,必须设置ulimit -c unlimited,另外在Linux或Solaris下可以通过kill -6 手工产生core dump。
高级用法
关于使用SA了解JAVA内存布局和解决问题,可以参考如下的文章:
- 借HSDB来探索HotSpot VM的运行时数据
- 借助HotSpot SA来一窥PermGen上的对象
- 怎么查看运行时常量池的数据?
- 代码样例
Javascript支持
SA提供了jseval指令,可以执行js代码,其中提供部分内置的js函数和全局变量,具体可以参见sun.jvm.hotspot.utilities.soql.JSJavascriptEngine的216~232行:
高级API
println("Function/Variable Description"); println("================= ==========="); println("address(jobject) returns the address of the Java object"); println("classof(jobject) returns the class object of the Java object"); println("dumpClass(jclass,[dir]) writes .class for the given Java Class"); println("dumpHeap([file]) writes heap in hprof binary format"); println("help() prints this help message"); println("identityHash(jobject) returns the hashCode of the Java object"); println("mirror(jobject) returns a local mirror of the Java object"); println("load([file1, file2,...]) loads Javascript file(s). With no files, reads "); println("object(string) converts a string address into Java object"); println("owner(jobject) returns the owner thread of this monitor or null"); println("sizeof(jobject) returns the size of Java object in bytes"); println("staticof(jclass, field) returns a static field of the given Java class"); println("read([prompt]) reads a single line from standard input"); println("quit() quits the interactive load call"); println("jvm the target jvm that is being debugged");
底层API
具体的实现代码参见sun.jvm.hotspot.utilities.soql包下面的sa.js,由于代码比较长,此处仅截取部分:
var sapkg = new Object();sapkg.hotspot = Packages.sun.jvm.hotspot;sapkg.asm = sapkg.hotspot.asm;sapkg.bugspot = sapkg.hotspot.bugspot;sapkg.c1 = sapkg.hotspot.c1;sapkg.code = sapkg.hotspot.code;sapkg.compiler = sapkg.hotspot.compiler;sapkg.interpreter = sapkg.hotspot.interpreter;sapkg.livejvm = sapkg.hotspot.livejvm;sapkg.jdi = sapkg.hotspot.jdi;sapkg.memory = sapkg.hotspot.memory;sapkg.oops = sapkg.hotspot.oops;sapkg.runtime = sapkg.hotspot.runtime;sapkg.tools = sapkg.hotspot.tools;sapkg.types = sapkg.hotspot.types;sapkg.ui = sapkg.hotspot.ui;sapkg.utilities = sapkg.hotspot.utilities;var sa = new Object();sa.vm = sapkg.runtime.VM.getVM();sa.dbg = sa.vm.getDebugger();sa.cdbg = sa.dbg.CDebugger;sa.heap = sa.vm.universe.heap();sa.systemDictiOnary= sa.vm.systemDictionary;sa.sysDict = sa.systemDictionary;sa.symbolTable = sa.vm.symbolTable;sa.symTbl = sa.symbolTable;sa.threads = sa.vm.threads;sa.interpreter = sa.vm.interpreter;sa.typedb = sa.vm.typeDataBase;sa.codeCache = sa.vm.codeCache;sa.objHeap = sa.vm.objectHeap;var OS = sa.vm.OS;var CPU = sa.vm.CPU;var LP64 = sa.vm.LP64;var isClient = sa.vm.clientCompiler;var isServer = sa.vm.serverCompiler;var isCore = sa.vm.isCore();var addressSize = sa.vm.addressSize;var oopSize = sa.vm.oopSize;
常用命令
1.查看内建函数和变量
jseval "help()"
jvm全局变量
属性名称 |
备注 |
addressSize |
32位还是64位 |
buildInfo |
jdk构建新型,包括版本号,构建日期,编译器等 |
cpu |
CPU类型,例如:x86_64 |
flags |
类似-XX:+PrintFlagsFinal效果 |
heap |
|
os |
操作系统信息,例如:solaris、linux、bsd、wind32等 |
sysProps |
返回jvm的系统属性,和jinfo命令看到的类似 |
threads |
线程列表 |
type |
Client、Server、Core |
version |
JDK版本 |
classpath |
java.class.path |
bootClasspath |
sun.boot.class.path |
userDir |
user.dir |
查看线程对象属性代码:
jseval "t=jvm.threads[jvm.threads.length-1]"jseval "for(k in t){print(k);print(',');}"
代码样例
jseval "st.stringsDo(function (s) { if (sapkg.oops.OopUtilities.stringOopToString(s).matches('^(a|b).')) {print(s + ': '); s.printValueOn(java.lang.System.out); println('')}})"jseval "java.lang.Long.toHexString(0x0000000193899df0+sapkg.oops.ConstantPool.headerSize*4)"
java -classpath .:$SAPATH/sa-jdi.jar sun.jvm.hotspot.tools.JMap -histo `pgrep java` > jmap_output.log
jstack -m $JAVA_HOME/bin/java core.11028
jstack有时候看不到具体的错误信息,可以通过开关
export LIBSAPROC_DEBUG=1打开