最近在网上看到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 java jps -mvl
-
attch进程
java -cp .:$JAVA_HOME/lib/sa-jdi.jar sun.jvm.hotspot.CLHSDB attach 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
高级用法
关于使用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;
// 'debugger' is a JavaScript keyword :-(
// sapkg.debugger = sapkg.hotspot.debugger;
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;
// SA singletons are kept in 'sa' object
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;
// 'objHeap' is different from 'heap'!.
// This is SA's Oop factory and heap-walker
sa.objHeap = sa.vm.objectHeap;
// few useful global variables
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打开