Java Class文件結構實例分析(上)

本文假定讀者對Java Class文件格式有一些基本的瞭解,建議結合相關書籍進行對照閱讀。java

Class文件格式信息

image

image

實例代碼

package chapter6;
public class TestClass {
	private int m;
	public int inc() {
		return m + 1;
	}
}
複製代碼

使用JDK1.8編譯成class文件,而後經過WinHex打開工具

image

魔數(magic)

類型:u4
字節地址:00000000~00000003
值:0xCAFEBABEthis

image

Class文件版本

次版本號(minor_version)

類型:u2
字節地址:00000004~00000005
值:0x0000編碼

主版本號(major_version)

類型:u2
字節地址:00000006~00000007
值:0x0034spa

將0x0034轉換爲十進制,計算獲得52,對應版本號爲JDK 1.8。3d

image

常量池

常量池容量計數值(constant_pool_count)

類型:u2
字節地址:00000008~00000009
值:0x0016code

將0x0016轉換爲十進制,計算獲得22。因爲容量計數是從1開始(若是沒有特殊狀況,一般都是從0開始),所以常量池中有21項常量,索引值範圍爲1~21。cdn

image

常量池中每一項常量都是一個表,表開始的第一位是一個u1類型的標誌位(tag)。blog

第1項常量

tag類型:u1
tag字節地址:0000000A
tag值:0x07繼承

查表可知這個常量屬於CONSTANT_Class_info結構,表明一個類或者接口的符號引用。

name_index類型:u2
name_index字節地址:0000000B~0000000C
name_index值:0x0002

0x0002指向了常量池中的第2項常量。

image

第2項常量

tag類型:u1
tag字節地址:0000000D
tag值:0x01

查表可知這個常量屬於CONSTANT_Utf8_info結構,表明一個UTF-8編碼的字符串。

image

length類型:u2
length字節地址:0000000E~0000000F
length值:0x0012

將0x0012轉換爲十進制,計算獲得18。

bytes類型:u1
bytes字節地址:00000010~00000021(length代表地址範圍爲18個字節)
bytes值:下方圖片淺藍底對應的全部字節內容

經過WinHex查看,對應內容爲chapter6/TestClass,即類的全限定名。

image

經過逐個字節對照ASCII字符表,咱們一樣能夠獲得內容爲chapter6/TestClass。

  • 獲取ASCII字符表:在Linux上執行man ascii,翻頁在Tables項能夠看到字符表。
  • 查找字符:先找橫座標,再找縱座標,橫豎交叉的位置即爲字節對應的字符。

例如0x63爲c,0x68爲h,0x61爲a,0x70爲p,0x74爲t,0x65爲e,0x72爲r,連起來表明單詞chapter。

image

第3項常量

tag類型:u1
tag字節地址:00000022
tag值:0x07

這個常量屬於CONSTANT_Class_info結構,表明一個類或者接口的符號引用。

name_index類型:u2
name_index字節地址:00000023~00000024
name_index值:0x0004

0x0004指向了常量池中的第4項常量。

第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,即類的全限定名。

image

第5項常量

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。

image

其餘常量能夠經過相似的方法進行分析,但這樣一個個分析確實挺辛苦的。

其實,JDK已經爲咱們提供了一個Class文件字節碼工具:javap,可讓咱們較爲直觀的看到Class文件的字節碼內容。

執行命令:javap -verbose TestClass.class,截取常量池部份內容以下:

image

能夠看到,版本號及前5個常量與咱們分析的結果是一致的。因此,能用1行代碼搞定的事兒,就不要用2行(浪費筆墨)。

常量池最後一個字節:000000D8

訪問標誌(access_flags)

類型:u2
字節地址:000000D9~000000DA
值:0x0021

查看類或接口訪問標誌含義表可知,該類的訪問標誌爲ACC_PUBLIC(0x0001)、ACC_SUPER(0x0020)。

image

另外,經過類的定義public class TestClass,一樣能夠推斷出類的訪問標誌爲ACC_PUBLIC、ACC_SUPER,而ACC_INTERFACE、ACC_ENUM、ACC_FINAL、ACC_ABSTRACT、ACC_ANNOTATION、ACC_SYNTHETIC均可以排除。

因此,access_flags應該爲0x0001|0x0020=0x0021,結果與查看字節碼相同。

類索引(this_class)

類型:u2
字節地址:000000DB~000000DC
值:0x0001

this_class指向常量池的第1個常量,基於前面的分析可知:

  • 第1個常量的類型爲Class,Class名稱索引指向第2個常量。
  • 第2個常量類型爲Utf8,對應內容爲chapter6/TestClass。

所以,類索引(this_class)指向的類爲chapter6/TestClass。

父類索引(super_class)

類型:u2
字節地址:000000DD~000000DE
值:0x0003

一樣,super_class指向常量池的第3個常量。

  • 第3個常量的類型爲Class,Class名稱索引指向第4個常量。
  • 第4個常量類型爲Utf8,對應內容爲java/lang/Object。

所以,父類索引(super_class)指向的類爲java/lang/Object。

接口計數器(interfaces_count)

類型:u2
字節地址:000000DF~000000E0
值:0x0000

接口計數器值爲0,說明該類沒有實現任何接口。

接口表(interfaces)

類索引(this_class)、父類索引(super_class)和接口索引(interfaces)這三項數據共同肯定了當前類以及其繼承關係,相關常量池內容以下:

image

完整地址範圍:000000DB~000000E0

image

字段

字段計數器(fields_count)

類型:u2
字節地址:000000E1~000000E2
值:0x0001

說明當前類有1個字段。

字段表(fields)

image

image

訪問標誌(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;

image

屬性計數器(attributes_count)

類型:u2
字節地址:000000E9~000000EA
值:0x0000

說明該字段沒有屬性信息。

屬性表(attributes)

無。

字段完整地址範圍:000000E1~000000EA

image

最後是方法和屬性,因爲內容複雜度及篇幅緣由,咱們下篇再續。


參考

《Java虛擬機規範》(Java SE 8版)

《深刻理解Java虛擬機 JVM高級特性與最佳實踐》

我的公衆號

更多文章,請關注公衆號:二進制之路

二進制之路
相關文章
相關標籤/搜索