Java字節(jié)碼的結(jié)構(gòu)

參考《Java虛擬機(jī)規(guī)范JavaSE7版》的描述來看,每一個字節(jié)碼文件其實都對應(yīng)著全局唯一的一個類或者接口的定義信息。字節(jié)碼文件采用的是一種類似于C語言結(jié)構(gòu)體的偽結(jié)構(gòu)來描述字節(jié)碼文件格式。為了避免與類的字段、實例等概念產(chǎn)生混淆,本書將用于描述類結(jié)構(gòu)格式的內(nèi)容定義為項(item)

每一項都包括類型、名稱以及該項的數(shù)量。類型可以是表明,也可以是“基本類型”。包含在字節(jié)碼文件中,各項按照嚴(yán)格的順序進(jìn)行連續(xù)存放,其內(nèi)部并不包含任何的分隔符區(qū)分段落。在此大家需要注意,這個結(jié)構(gòu)體中只有兩種數(shù)據(jù)結(jié)構(gòu),分別是無符號數(shù)和表,其中無符號數(shù)屬于字節(jié)碼文件中的“基本類型”,字節(jié)碼文件中的無符號數(shù)有u1/u2/u4/u8,分別表示一個字節(jié)無符號類型、兩個字節(jié)無符號類型、4個字節(jié)無符號類型、8個字節(jié)無符號類型。

表是由多個無符號數(shù)或者其他表作為數(shù)據(jù)項構(gòu)成的復(fù)合數(shù)據(jù)類型,所有表的后綴都是使用“_info”進(jìn)行結(jié)尾,并且字節(jié)碼文件實質(zhì)上也就是一張表。每一個字節(jié)碼文件對應(yīng)著一個ClassFile的結(jié)構(gòu),如下所示:

Class{

u4??? ??????????magic

u2??? ??????????minor_version

u2?????????????major_version

u2??? ??????????constant_pool_count

cp_info????????? constant_pool[constant_pool_count-1]

u2????????????? access_flags

u2????????????? this_class

u2????????????? super_class

u2????????????? interfaces_count

u2????????????? interfaces[interfaces_count]

u2????????????? fields_count

field_info??????? fields[fields_count]

u2????????????? methods_count

method_info???? methods[methods_count]

u2????????????? attributes_count

attribute_info???? attributes[attributes_count]

}

關(guān)于ClassFile結(jié)構(gòu)的描述信息,如下所示:

1)? magic 魔術(shù)字符

一個有效地字節(jié)碼文件的前4個字節(jié)為0xCAFEBABE,(咖啡寶貝),也稱之為魔術(shù)字符。JVM用魔術(shù)字符來校驗一個目標(biāo)class文件是否是合法的。

2)? minor_version(此版本號)和major_version(主版本號)

緊跟在magic之后的4個字節(jié)就是編譯的次版本號和主版本號,他們共同構(gòu)成了字節(jié)碼文件的版本號。如果字節(jié)碼文件的版本號超出了JVM所能夠處理的有效范圍,那么Java虛擬機(jī)將不會處理這個字節(jié)碼文件。不過高版本的JVM卻能向下兼容運(yùn)行由低版本JDK編譯的字節(jié)碼文件。

3)? constant_pool_count(常量池計數(shù)器)和constant_pool(常量池)

在字節(jié)碼文件中,緊跟在次版本號和主版本號之后的就是常量池計數(shù)器和常量池。常量池是字節(jié)碼文件中非常重要的數(shù)據(jù)項,同時也是字節(jié)碼文件中與其他項關(guān)聯(lián)最大和占用字節(jié)碼空間最大的數(shù)據(jù)項。常量池主要存放字面量(Literal)和符號引用(Symbolic References)兩大類數(shù)據(jù)常量,其訪問方式是通過索引來進(jìn)行訪問的,但由于常量池列表中的數(shù)量并不固定,因此在常量池之前就需要通過一個2個字節(jié)的常量池計數(shù)器來統(tǒng)計常量池列表中到底擁有多少常量項。在此大家注意,常量池計數(shù)器中的計數(shù)值并不是從0開始進(jìn)行計數(shù)的,而從1開始,也就是說,如果常量池中有兩個常量時,計數(shù)值為2。

常量池中存放的字面量由文字字符串、final常量值等構(gòu)成,而符號引用則包括了類和接口的全限定名(Fully Qualified Name)、字段的名稱和描述符(Descriptor),以及方法的名稱和描述符。

4)? access_flags(訪問標(biāo)志)

緊跟在常量池之后的2個字節(jié)是訪問標(biāo)志,訪問標(biāo)志就是用于表示某個類或者接口的訪問權(quán)限。比如:訪問標(biāo)志指明的是字節(jié)碼文件中的類還是接口;使用的訪問修飾符是哪一種,是否是由abstract關(guān)鍵字修飾的抽象類;如果是被abstract修飾的抽象類,不能再標(biāo)記為final類型;接口類型同樣也不允許被final修飾。訪問標(biāo)志的定義如下所示(僅列舉2項,具體請看書):

訪問標(biāo)志

描述

ACC_PUBLIC

0x0001

聲明為public,可以被包的類進(jìn)行外訪問

ACC_FINAL

0x0010

聲明為final,不允許有派生類

5)? this_class(類索引)和super_class(超類索引)

緊跟在訪問標(biāo)志之后的4個字節(jié)就是類索引和超類索引,類索引和超類索引各自會通過索引指向常量池列表中的一個類型為CONSTANT_Class_info的常量項。CONSTANT_Class_info由tag和name_index兩部分組成,tag是一個具有CONSTANT_Class_info值的常量,而name_index則是指向常量池列表中類型為CONSTANT_Utf8_info常量項的索引,通過這個索引即可成功獲取到CONSTANT_Utf8_info常量項中的全限定名字符串,如下圖所示。簡單來說,類索引用于確定當(dāng)前類的全限定名,而超類索引則用于確定當(dāng)前類的超類的全限定名。

6)? interfaces_count(接口計數(shù)器)和interface(接口表)

在類索引和超類索引之后的4個字節(jié)就是接口計數(shù)器和接口表。接口計數(shù)器用于表示當(dāng)前類或者接口的直接超類接口數(shù)量。接口表實際上是一個數(shù)組集合,包含了當(dāng)前類或者接口在常量池列表中直接超類接口的索引集合,通過這個索引即可確定當(dāng)前類或者接口的超類接口的全限定名。

7)? fields_count(字段計數(shù)器)和fields(字段表)

在接口計數(shù)器和接口表之后就是字段計數(shù)器和字段表。字段計數(shù)器用于表示一個字節(jié)碼文件中的field_info表總數(shù),也就是一個類中類變量和實例變量的數(shù)量總和。而字段表實際上則是一個數(shù)組集合,字段表中的每一個成員都必須是一個field_info結(jié)構(gòu)的數(shù)據(jù)項。簡單來說,field_info用于表達(dá)一個字段的完整信息,比如字段的表示符、訪問修飾符(public/private/protected)、是類變量還是實例變量(static 修飾符)、是否是常量(final修飾符)。字段表中所包含的字段信息僅限于當(dāng)前類或接口的所屬字段,并不包含繼承超類后的字段信息。

8)? methods_count(方法計數(shù)器)和methods(方法表)

在字段計數(shù)器和字段表之后就是方法計數(shù)器和方法表。方法計數(shù)器用于表示一個字節(jié)碼文件中的method_info表總數(shù)。而方法表實際上是一個數(shù)組集合,方法表中的每個成員都必須是一個method_info結(jié)構(gòu)的數(shù)據(jù)項。簡單來說,method_info用于表示當(dāng)前類或者接口中某個方法的完整描述,比如方法標(biāo)示符、方法的訪問修飾符、方法的返回值類型以及方法的參數(shù)信息等。方法表中所包含的方法信息僅限于當(dāng)前類或者接口中的所屬方法,并不包含繼承超類后的方法信息。

9)? attribute_count(屬性計數(shù)器)和attributes(屬性表)

在方法計數(shù)器和方法表之后的就是屬性計數(shù)器和屬性表。屬性計數(shù)器用于表示當(dāng)前字節(jié)碼文件中的attribute_info表總數(shù)。而屬性表同之前的字段表和方法表一樣都是一個數(shù)組集合,屬性表中的每一個成員都必須是一個attribute_info結(jié)構(gòu)的數(shù)據(jù)項。每一個attribute_info表的第一項都是指向常量池列表中的CONSTANT_Utf8_info項的索引,該表給出了屬性的名稱。

屬性可以出現(xiàn)在ClassFile表、字段表和方法表中,用以描述與其相關(guān)的信息,比如描述字節(jié)碼文件中所定義的類和接口相關(guān)的信息、描述與字段相關(guān)的信息、描述與方法相關(guān)的信息。

摘自《Java虛擬機(jī)精講》高翔龍

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容