JDK中的SA(ServiceAbility)工具介绍

最近在网上看到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权限,否则会报错

调试本地进程

  1. 获取要调试的进程ID:

    ps -ef|grep java
    jps -mvl
    
  2. 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
    

调试远程进程

  1. 远程机器上启动rmiregistry服务;

    rmiregistry -J-d64 -J-Xbootclasspath/p:${JAVA_HOME}/lib/sa-jdi.jar
                    或
    rmiregistry -J-Xbootclasspath/p:${JAVA_HOME}/lib/sa-jdi.jar
    
  2. 启动debug server:

客户端通过机器名称连接到debug server进行调试,默认情况下,当只启动一个debug server时,机器名称为ip地址;

 ```bash
 java -d64 -classpath ${JAVA_HOME}/lib/sa-jdi.jar sun.jvm.hotspot.jdi.SADebugServer <pid>
                或
 java -d64 -classpath ${JAVA_HOME}/lib/sa-jdi.jar sun.jvm.hotspot.jdi.SADebugServer <java executable> <core file>
 ```

调试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 <pid>手工产生core dump。

高级用法

关于使用SA了解JAVA内存布局和解决问题,可以参考如下的文章:

  1. 借HSDB来探索HotSpot VM的运行时数据
  2. 借助HotSpot SA来一窥PermGen上的对象
  3. 怎么查看运行时常量池的数据?
  4. 代码样例

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 <stdin>");
        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打开

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,922评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,591评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,546评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,467评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,553评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,580评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,588评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,334评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,780评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,092评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,270评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,925评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,573评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,194评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,437评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,154评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352

推荐阅读更多精彩内容