先看一段代码
Test.java:java
public class Test { static Test2 t1 = new Test2(); Test2 t2 = new Test2(); public void fn() { Test2 t3 = new Test2(); } } class Test2 { }
Main.java:jvm
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 -XX:+UseSerialGC -Xmx10m
启动jdb(命令的含义是使用SerialGC,同时设置java heap的大小10m)stop in Test.fn
Test的在方法fn处设置断点run Main
指定主类,启动java程序next
向前执行一步java -cp sa-jdi.jar sun.jvm.hotspot.HSDB
启动HSDB默认打开的窗口是java Threads窗口,显示的是线程列表,双击表明线程的行会打开Oop Inspector窗口,显示HotSpot VM里记录线程的一些基本信息的C++对象的内容工具
选中java Threads Name 等于Main一行,而后点击Java Threads窗口工具栏中从左数第二个按钮能够打开Stack Memory窗口来查看main线程的栈:oop
Stack Memory窗口有三列:
左起第一列是内存地址,本文中所提到的内存地址都是虚拟内存地址不是物理内存地址
左起第二列是该地址上存储的数据,以字宽为单位,本文例子中是在Windows 7 64-bit上跑64位的JDK7的HotSpot VM,字宽是64位(8字节)
左起第三列是对数据注释,竖线是范围,横线或斜线是链接范围和注释文字布局
在HSDB界面选择:Window -> Control就能够打开HSDB的控制台了,敲一下回车就能够看见hsdb>提
示符,此时就可使用命令来查看更多详细信息了:spa
不知道有哪些命令,能够试试help
:线程
universe
命令查看GC heap的使用范围和使用状况
这里咱们能够很清晰的看到GC堆由:Young Gen 和 Old Gen构成,还能够看到各区的初始大小和使用状况调试
在咱们的java代码中当执行完test.fn时应该建立了三个Test2的实例和一个Test的实例,那么他们都在那呢?下面让咱们用scanoops命令来把他们找出来:code
这里能够看出确实扫描出了三个Test2类型的实例和一个Test类型的实例,内容有两列:左列是实例的起始地址,右列是实例的实际类型。从它们的起始地址,对照前面使用universe命令看到的GC堆的地址范围,能够看出他们都在Eden里。对象
whatis
命令能够进一步看到它们都分配到了main线程的Thread-local Allocation Buffer中:inspect
命令查看对象的内容:
1> instance of Oop for Test: 代表该地址表明的对象是Test类的实例
2> _mark: 对象头的第一个字段记录对象的状态
3> _metadata._compressed_klass: 指向描述Test类信息的对象
4> t2:Oop for Test2: 该实例的字段t2是Test2的实例
mem
能够看更直接的数据,接受的两个参数,起始地址和以字宽为单位的“长度”hsdb> mem 0x00000000ff6de2e0 20x00000000ff6de2e0: 0x0000000000000001 // _mark0x00000000ff6de2f8: 0x0000000011ba0418 // _metadata._compressed_klass