先看一段代码
Test.java:
public class Test {
static Test2 t1 = new Test2();
Test2 t2 = new Test2();
public void fn() {
Test2 t3 = new Test2();
}
}
class Test2 {
}
Main.java:
public class Main {
public static void main(String[] args) {
Test test = new Test();
test.fn();
}
}
众所周知执行Main类中的main方法会在java
heap中生成三个Test2类的实例:t1,t2,t3和一个Test类的实例:test。那么这四个对象在javaheap中是如何布局的呢?下面我们使用HSDB来一探究竟。想要查看jvm运行时数据我们需要使程序刚好运行完test.fn
暂停下来。平时可能我们使用Eclipse,IDEA等各种IDE来调试,为减少对外部工具的依赖,我们使用Oracle JDK自带的工具jdb来完成次任务
一、启动jdb调试程序:
- 使用:
jdb -XX:+UseSerialGC -Xmx10m
启动jdb(命令的含义是使用SerialGC,同时设置java heap的大小10m) - 使用:
stop in Test.fn
Test的在方法fn处设置断点 - 使用:
run Main
指定主类,启动java程序 - 使用:
next
向前执行一步
二、启动HSDB
- 1、使用:
java -cp sa-jdi.jar sun.jvm.hotspot.HSDB
启动HSDB
- 2、使用jps查看第一部中启动的java程序的pid
- 3、在HSDB可视化界面的菜单里选择File -> Attach to HotSpot
process,在弹出的对话框中输入上一步找到的pid,点击Ok就连接到目标程序了
默认打开的窗口是java Threads窗口,显示的是线程列表,双击代表线程的行会打开Oop Inspector窗口,显示HotSpot VM里记录线程的一些基本信息的C++对象的内容
选中java Threads Name 等于Main一行,然后点击Java Threads窗口工具栏中从左数第二个按钮可以打开Stack Memory窗口来查看main线程的栈:
Stack Memory窗口有三列:
左起第一列是内存地址,本文中所提到的内存地址都是虚拟内存地址不是物理内存地址
左起第二列是该地址上存储的数据,以字宽为单位,本文例子中是在Windows 7 64-bit上跑64位的JDK7的HotSpot VM,字宽是64位(8字节)
左起第三列是对数据注释,竖线是范围,横线或斜线是连接范围和注释文字
三、下面让我们打开HSDB里的控制台来使用命令了解更多信息
在HSDB界面选择:Window -> Control就可以打开HSDB的控制台了,敲一下回车就可以看见hsdb>提
示符,此时就可以使用命令来查看更多详细信息了:
不知道有哪些命令,可以试试help
:
- 1、使用
universe
命令查看GC heap的使用范围和使用情况
这里我们可以很清晰的看到GC堆由:Young Gen 和 Old Gen构成,还可以看到各区的初始大小和使用情况
- 2、使用`scanoops命令查看指定类型的实例,scanoops命令接收两个必填参数和一个可选参数:必填参数是要扫描的地址范围,一个是起始地址一个结束地址,可选参数是要扫描什么类型的实例。实际扫描中会扫到指定类型及其派生类的实例。
在我们的java代码中当执行完test.fn时应该创建了三个Test2的实例和一个Test的实例,那么他们都在那呢?下面让我们用scanoops命令来把他们找出来:
这里可以看出确实扫描出了三个Test2类型的实例和一个Test类型的实例,内容有两列:左列是实例的起始地址,右列是实例的实际类型。从它们的起始地址,对照前面使用universe命令看到的GC堆的地址范围,可以看出他们都在Eden里。
- 3、使用
whatis
命令可以进一步看到它们都分配到了main线程的Thread-local Allocation Buffer中:
- 4、还可以使用
inspect
命令查看对象的内容:
1> instance of Oop for Test: 表明该地址代表的对象是Test类的实例
2> _mark: 对象头的第一个字段记录对象的状态
3> _metadata._compressed_klass: 指向描述Test类信息的对象
4> t2:Oop for Test2: 该实例的字段t2是Test2的实例
- 5、使用
mem
可以看更直接的数据,接受的两个参数,起始地址和以字宽为单位的“长度”
hsdb> mem 0x00000000ff6de2e0 2
0x00000000ff6de2e0: 0x0000000000000001 // _mark
0x00000000ff6de2f8: 0x0000000011ba0418 // _metadata._compressed_klass