先看一段代碼
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