本文假定讀者對Java Class文件格式有一些基本的瞭解,建議結合相關書籍進行對照閱讀。java
package chapter6;
public class TestClass {
private int m;
public int inc() {
return m + 1;
}
}
複製代碼
使用JDK1.8編譯成class文件,而後經過WinHex打開工具
類型:u4
字節地址:00000000~00000003
值:0xCAFEBABEthis
類型:u2
字節地址:00000004~00000005
值:0x0000編碼
類型:u2
字節地址:00000006~00000007
值:0x0034spa
將0x0034轉換爲十進制,計算獲得52,對應版本號爲JDK 1.8。3d
類型:u2
字節地址:00000008~00000009
值:0x0016code
將0x0016轉換爲十進制,計算獲得22。因爲容量計數是從1開始(若是沒有特殊狀況,一般都是從0開始),所以常量池中有21項常量,索引值範圍爲1~21。cdn
常量池中每一項常量都是一個表,表開始的第一位是一個u1類型的標誌位(tag)。blog
tag類型:u1
tag字節地址:0000000A
tag值:0x07繼承
查表可知這個常量屬於CONSTANT_Class_info結構,表明一個類或者接口的符號引用。
name_index類型:u2
name_index字節地址:0000000B~0000000C
name_index值:0x0002
0x0002指向了常量池中的第2項常量。
tag類型:u1
tag字節地址:0000000D
tag值:0x01
查表可知這個常量屬於CONSTANT_Utf8_info結構,表明一個UTF-8編碼的字符串。
length類型:u2
length字節地址:0000000E~0000000F
length值:0x0012
將0x0012轉換爲十進制,計算獲得18。
bytes類型:u1
bytes字節地址:00000010~00000021(length代表地址範圍爲18個字節)
bytes值:下方圖片淺藍底對應的全部字節內容
經過WinHex查看,對應內容爲chapter6/TestClass,即類的全限定名。
經過逐個字節對照ASCII字符表,咱們一樣能夠獲得內容爲chapter6/TestClass。
例如0x63爲c,0x68爲h,0x61爲a,0x70爲p,0x74爲t,0x65爲e,0x72爲r,連起來表明單詞chapter。
tag類型:u1
tag字節地址:00000022
tag值:0x07
這個常量屬於CONSTANT_Class_info結構,表明一個類或者接口的符號引用。
name_index類型:u2
name_index字節地址:00000023~00000024
name_index值:0x0004
0x0004指向了常量池中的第4項常量。
tag類型:u1
tag字節地址:00000025
tag值:0x01
這個常量屬於CONSTANT_Utf8_info結構,表明一個UTF-8編碼的字符串。
length類型:u2
length字節地址:00000026~00000027
length值:0x0010
將0x0010轉換爲十進制,計算獲得16。
bytes類型:u1
bytes字節地址:00000028~00000037(length代表地址範圍爲16個字節)
bytes值:下方圖片淺藍底對應的全部字節內容
經過WinHex查看,對應內容爲java/lang/Object,即類的全限定名。
tag類型:u1
tag字節地址:00000038
tag值:0x01
這個常量屬於CONSTANT_Utf8_info結構,表明一個UTF-8編碼的字符串。
length類型:u2
length字節地址:00000039~0000003A
length值:0x0001
bytes類型:u1
bytes字節地址:0000003B(length代表地址範圍爲1個字節)
bytes值:0x6D
經過WinHex查看,對應內容爲實例變量m。
其餘常量能夠經過相似的方法進行分析,但這樣一個個分析確實挺辛苦的。
其實,JDK已經爲咱們提供了一個Class文件字節碼工具:javap,可讓咱們較爲直觀的看到Class文件的字節碼內容。
執行命令:javap -verbose TestClass.class,截取常量池部份內容以下:
能夠看到,版本號及前5個常量與咱們分析的結果是一致的。因此,能用1行代碼搞定的事兒,就不要用2行(浪費筆墨)。
常量池最後一個字節:000000D8
類型:u2
字節地址:000000D9~000000DA
值:0x0021
查看類或接口訪問標誌含義表可知,該類的訪問標誌爲ACC_PUBLIC(0x0001)、ACC_SUPER(0x0020)。
另外,經過類的定義public class TestClass,一樣能夠推斷出類的訪問標誌爲ACC_PUBLIC、ACC_SUPER,而ACC_INTERFACE、ACC_ENUM、ACC_FINAL、ACC_ABSTRACT、ACC_ANNOTATION、ACC_SYNTHETIC均可以排除。
因此,access_flags應該爲0x0001|0x0020=0x0021,結果與查看字節碼相同。
類型:u2
字節地址:000000DB~000000DC
值:0x0001
this_class指向常量池的第1個常量,基於前面的分析可知:
所以,類索引(this_class)指向的類爲chapter6/TestClass。
類型:u2
字節地址:000000DD~000000DE
值:0x0003
一樣,super_class指向常量池的第3個常量。
所以,父類索引(super_class)指向的類爲java/lang/Object。
類型:u2
字節地址:000000DF~000000E0
值:0x0000
接口計數器值爲0,說明該類沒有實現任何接口。
無
類索引(this_class)、父類索引(super_class)和接口索引(interfaces)這三項數據共同肯定了當前類以及其繼承關係,相關常量池內容以下:
完整地址範圍:000000DB~000000E0
類型:u2
字節地址:000000E1~000000E2
值:0x0001
說明當前類有1個字段。
訪問標誌(access_flags)
類型:u2
字節地址:000000E3~000000E4
值:0x0002
對應的訪問標誌爲ACC_PRIVATE。
名稱索引(name_index)
類型:u2
字節地址:000000E5~000000E6
值:0x0005
對應常量池中的第5項常量,即字段名爲m。
描述符(descriptor_index)
類型:u2
字節地址:000000E7~000000E8
值:0x0006
對應常量池中的第6項常量,值爲I,即int類型。
所以,該字段的定義爲private int m;
類型:u2
字節地址:000000E9~000000EA
值:0x0000
說明該字段沒有屬性信息。
無。
字段完整地址範圍:000000E1~000000EA
最後是方法和屬性,因爲內容複雜度及篇幅緣由,咱們下篇再續。
參考
《Java虛擬機規範》(Java SE 8版)
《深刻理解Java虛擬機 JVM高級特性與最佳實踐》
更多文章,請關注公衆號:二進制之路