jdk中额外提供了很多工具,其中HSDB就是一款可用于调试jvm运行时数据的图形化工具,存在于jdk目录lib文件夹下的sa-jdi.jar中
启动方式
如果出现加载sawindbg.dll失败,那么可以去 jdk目录下的jre\bin文件夹下找到该文件复制到jre目录
编写测试用例
该测试代码为深入理解Java虚拟机书中的例子
package com.jvm;
public class HsdbTest {
static class Test {
static ObjectHolder staticObj = new ObjectHolder();
ObjectHolder instanceObj = new ObjectHolder();
void foo() {
ObjectHolder localObj = new ObjectHolder();
System.out.println("done");//为了不让程序终止,在这里加入断点
}
}
private static class ObjectHolder {
}
public static void main(String[] args) {
Test test = new Test();
test.foo();
}
}
通过jps找到该程序的id为10028
这里显示的就是新生代(Eden、Survivor From、Survivor To)以及老年代的虚拟内存地址起始范围
接下来打开windows->console窗口,输入命令
scanoops 0x000000076ba00000 0x0000000770400000 com.jvm.HsdbTest$ObjectHolder
由图可以看出,上述代码中staticObj、instanceObj、localObj三个变量的对象实例都存储在堆中
然后点击Tools->Inspector,可以查看对象头和指向对象元数据的指针,包含了Java类型的名字、继承关系、实现接口关系,字段信息、方法信息、运行时常量池的指针、内嵌的虚方法表(vtable)以及接口放发表(itable)
接着根据堆中对象实例地址找出引用它们的指针,使用Tools->Compute Reverse Ptrs或者在console中使用命令revptrs 0x000000076baec540来查看
然后在java.lang.Class的实例中找到了对该对象的引用
从《Java虚拟机规范》所定义的模型看,所有Class相关的信息都应该存放在方法区中,但具体如何实现却并未规定。JDK7及其以后版本的HotSpot虚拟机选择把静态变量与类型在Java语言一端的映射Class对象存放在一起,存储于Java堆中
而第二个对象实例(instanceObj)就是指向了Test类了
第三个对象实例(localObj)由于是在方法中,所以是Java栈的栈帧中的局部变量表在引用这个对象实例,而revptrs命令并不支持查找栈上的指针引用,所以返回了null
不过在简单代码中还可以人工来查找,通过下图在该线程的栈内存中找到了有个地方引用了该对象实例内存地址的值
且已经生成了注释,这里引用了来自年轻代的com/jvm/HeadTest$Test对象实例
以上就是根据一个类中三个对象的定位来演示HSDB工具的例子
总结:
1.HSDB工具的大概使用方法
2.JDK7之后,静态变量被Class对象引用,且都存放于Java堆中
3.对象实例存放于堆中
4.方法中的对象变量存放于虚拟机栈的栈帧中的局部变量表中,通过指针指向Java堆中的对象实例数据(当然这里的引用可能分为句柄引用和指针引用,不过hotspot虚拟机一般采用的是指针引用)
引申:
JDK9以后,jmap、jstat、jmap等工具都移入到jhsdb中了,不需要再单独使用各自的命令了