目录
一、HSDB
1、测试用例:
2、Java Threads窗口
3、Tools 选项
4、windows选项
二、CLHSDB
1、threads和thread
2、classes和class
3、inspect
4、 jstack
5、universe
6、scanoops
7、revptrs
8、mem
9、print
10、where
11、printas
12、printstatics
13、printmdo
三、JHSDB
package jvmTest;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
interface interTest{
void show();
}
class Base{
public int a;
public Base(int a) {
this.a = a;
}
}
class A extends Base implements interTest {
public int b;
public A(int a,int b) {
super(a);
this.b=b;
}
@Override
public void show() {
System.out.println("a->"+a+",b="+b);
}
}
public class MainTest {
public static void main(String[] args) {
String s="shl";
String[] s2={"shl","abc","bcd"};
A a=new A(1,2);
a.show();
while (true){
try {
System.out.println(getProcessID());
Thread.sleep(600*1000);
} catch (Exception e) {
}
}
}
public static final int getProcessID() {
RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
System.out.println(runtimeMXBean.getName());
return Integer.valueOf(runtimeMXBean.getName().split("@")[0])
.intValue();
}
}
执行main方法后就可从控制台获取进程ID。
进入JAVA_HOME的lib目录下,在命令行执行java -cp ./sa-jdi.jar sun.jvm.hotspot.HSDB就可唤起HSDB的图形界面,点击File-》Attach to Hotspot Process,输入进程ID,点击OK,
第一次使用时会报错,如下图:
这时将jdk/jre/bin目录下的sawindbg.dll拷贝到该目录下即可,重启,attach成功后进入如下界面:
该界面显示了当前Java进程下的几个子线程,main线程就是执行main方法的用户线程,另外5个是JVM自身使用的线程,选中main线程,上述的5个按钮就都可以点击了,第一个按钮Inspect Thread是查看选中的线程对应的java.lang.Thread对象,如下图:
可以层层展开查看该对象的各属性,也可改变地址框中的对象地址,查看特定引用对象的属性
第二个按钮Stack Memery是查看当前线程的调用栈的内存,如下图所示:
一共有3列,第一列是虚拟内存地址,第二列是该内存地址上的数据,以字宽为单位,64位CPU下就是8字节,即以该地址为起始往后8字节的数据,第三列是对内存数据的注释,同颜色的竖线表示范围,横线或斜线连接范围与注释文字,Interpreted frame表示一个调用栈帧,第一个对应sleep方法的调用栈帧,第二个对应main方法的调用栈帧,最下面的是main方法创建的局部变量的地址,比较奇怪的是代码中只创建了一个String[],这里却有两个ObjAarray了?还有一个是main方法的参数String[] args。
第三个按钮show Java stack trace是显示当前线程的调用链,点击其中的方法可查看方法的字节码,如main方法,注意本地方法没有字节码所以查看不了,如下图:
其中pc表示具体的方法字节码指令地址,将滑块拖到最下面点击Constant Pool,可以查看常量池中的具体内容,如下:
第四个按钮Show thread infomation可用于查看指定线程的信息,如下图:
State是线程的状态,Stack in use是线程调用栈占用的内存的起始地址,Base of Stack是调用栈的基地址,Last_Java_SP是调用栈的当前栈帧的栈顶地址,Last_Java_SP表示调用栈的当前栈帧的栈基地址,Last_Java_PC是上一次执行的字节码指令的地址。
最后一个按钮find crashes是查找崩溃的线程。
@后面就该该类的类型信息或者方法的字节码指令的内存地址,点击搜索结果,可以查看该类的继承关系,方法列表,属性列表,点击方法可查看字节码,拉到底部可查看该类的常量池,如下图:
windows选项下包含两个,console和Debugger console,前者是hsdb命令行控制台,后者是hsdb自身调试用的控制台。前者实际是调用了hsdb的命令行版本CLHSDB,提供了更丰富强大灵活的命令,输入help,查看所有的命令:
参考:Java Attach机制
JVM源码分析之Attach机制实现完全解读
进入JAVA_HOME的lib目录下,在命令行执行java -cp ./sa-jdi.jar sun.jvm.hotspot.CLHSDB就可唤起CLHSDB的命令行界面了,执行attach 进程ID可attach到本地或者远程的java进程,采用跟HSDB同样的测试用例。
输入threads可以查看所有的子线程,输入thread 线程id可以查看该线程的详情
第一行Thread 1 Address是Thread实例的地址。
classes是列出已经加载的所有的类的类型信息,class 完整类名是查找该类的类型信息,如下图:
同图形界面的Inspect,用于查看指定地址的类(C++的类)的各属性信息,如下图:
jstack用于查看是否存在死锁,查看所有线程的调用栈,加上-v选项可以输出详细的内存地址信息,如下图:
universe同图形界面中的Heap Paramters选项,显示年轻代和老年代堆内存的地址范围,如下图:
用于在指定地址范围内搜索所有指定类型的所有实例(Oop),后跟起始地址和类型信息,然后通过inspect 可查看具体的实例属性,如下图:
revptrs可根据对象地址查看引用该对象的活跃对象的地址,这里的引用是指通过类全局属性而非局部变量引用,修改上述测试用例在类A中增加一个私有属性,private Base ba=new Base(1);,然后依次执行universe,scanoops,revptrs,inspect命令,如下图:
mem命令可查看指定起始地址和以位宽为单位的长度的内存的数据,64位CPU的位宽是8字节,如下图:
A实例的内存大小是24字节,依次是8字节的对象头,4字节的属性a,4字节的指向Kclass的压缩指针,4字节的指向Base实例的压缩指针,4字节的属性b。
其他命令可参考OpenJDK 下hotspot/agent/src/share/sun/jvm/hotspot/CommandProcessor的实现。
输入一个Klass*, Method*的地址,可以打印该类或者方法,效果等同于Code Viewer选项,如下图:
通过threads可查看所有的线程,输入线程id,查看该线程的调用栈,输入-a,查看所有线程的调用栈,如下图:
后跟一个Hotspot Type和地址,会打印该Type对象的各属性,效果同inspect命令,不过不局限与oop,也可以是对象的真实地址。如下图:
printstatics 可以用于获取Hotspot 定义的C++类的静态属性,如表示Java堆内存的Universe对象,如下图:
printmdo用于打印指定地址的MethodData对象,该对象保存了Profile统计的方法性能的数据,如下图:
参考:014-通过JDB调试,通过HSDB来查看HotSpot VM的运行时数据
HSDB(查看对象内存HSDB)
通过HSDB来了解String值的真身在哪里
JDK中的SA(ServiceAbility)工具介绍
Java9为了简化hsdb和clhsdb的使用引入了一个新的命令jhsdb,可以通过该命令直接调用hsdb,clhsdb,jstack, jmap等命令,如下图:
OpenJDK8 的bin目录:
参考:Oracle-jhsdb