?每一個class文件都對應(yīng)著唯一一個類或者接口的定義信息诫欠,但是相對地纵揍,類或者接口并不一定都必須定義在文件里(比如類或者接口也可以通過類加載器直接生成)包个。我們通俗地將任意一個有效的類或者接口所應(yīng)當(dāng)滿足的格式稱為“class文件格式”私股,即使它不一定以磁盤文件的形式存在摹察。
?Class文件是有8個字節(jié)為基礎(chǔ)的字節(jié)流構(gòu)成的,這些字節(jié)流之間都嚴(yán)格按照規(guī)定的順序排列倡鲸,并且字節(jié)之間不存在任何空隙供嚎,對于超過8個字節(jié)的數(shù)據(jù),將按 照Big-Endian的順序存儲峭状,也就是說高位字節(jié)存儲在低的地址上面克滴,而低位字節(jié)存儲到高地址上面,其實這也是class文件要跨平臺的關(guān)鍵优床,因為 PowerPC架構(gòu)的處理采用Big-Endian的存儲順序劝赔,而x86系列的處理器則采用Little-Endian的存儲順序,因此為了Class文 件在各中處理器架構(gòu)下保持統(tǒng)一的存儲順序胆敞,虛擬機規(guī)范必須對起進(jìn)行統(tǒng)一着帽。在Java JDK中,可以使用java.io.DataInput移层、java.io.DataOutput等接口和java.io.DataInputStream和java.io.DataOutputStream等類來訪問這種格式的數(shù)據(jù)仍翰。
?Class文件結(jié)構(gòu)采用類似C語言的結(jié)構(gòu)體來存儲數(shù)據(jù)的,主要有兩類數(shù)據(jù)項观话,無符號數(shù)和表予借,無符號數(shù)用來表述數(shù)字,索引引用以及字符串等,比如 u1,u2,u4,u8分別代表1個字節(jié)灵迫,2個字節(jié)秦叛,4個字節(jié),8個字節(jié)的無符號數(shù)瀑粥,而表是有多個無符號數(shù)以及其它的表組成的復(fù)合結(jié)構(gòu)挣跋。
一、class的文件結(jié)構(gòu)
類型 | 名稱 | 數(shù)量 | |
---|---|---|---|
u4 | magic | 1 | |
u2 | minor_version | 1 | |
u2 | major_version | 1 | |
u2 | constant_pool_count | 1 | |
cp_info | constant_pool | constant_pool_count - 1 | |
u2 | access_flags | 1 | |
u2 | this_class | 1 | |
u2 | super_class | 1 | |
u2 | interfaces_count | 1 | |
u2 | interfaces | interfaces_count | |
u2 | fields_count | 1 | |
field_info | fields | fields_count | |
u2 | methods_count | 1 | |
method_info | methods | methods_count | |
u2 | attributes_count | 1 | |
attribute_info | attributes | attributes_count |
1.1 魔數(shù)(u4 magic)
?每個Class文件的頭4個字節(jié)稱為魔數(shù)(magic)利凑,它的唯一作用是判斷該文件是否為一個能被虛擬機接受的Class文件。它的值固定為0xCAFEBABE嫌术。
1.2 class文件版本
u2 minor_version
:副版本號
u2 major_version
:主版本號
?主副版本號共同構(gòu)成了 Class 文件的格式版本號哀澈。譬如某個 Class 文件的主版本號為 M,副版本號為 m度气,那么這個Class 文件的格式版本號就確定為 M.m割按。一個 Java 虛擬機實例只能支持特定范圍內(nèi)的主版本號。不同版本的Java編譯器編譯的Class文件對應(yīng)的版本是不一樣的磷籍。高版本的虛擬機支持低版本的編譯器編譯的 Class文件結(jié)構(gòu)适荣。比如Java SE 6.0對應(yīng)的虛擬機支持Java SE 5.0的編譯器編譯的Class文件結(jié)構(gòu),反之則不行院领。
1.3 常量池計數(shù)器(constant_pool_count )
?常量池計數(shù)器弛矛,constant_pool_count 的值等于 constant_pool 表中的成員數(shù)加 1。constant_pool 表的索引值只有在大于 0 且小于 constant_pool_count 時才會被認(rèn)為是有效的比然。(0表示不引用常量池的任一項)
1.4 常量池(constant_pool)
?major_version之后是常量池(constant_pool)的入口丈氓,它是Class文件中與其他項目關(guān)聯(lián)最多的數(shù)據(jù)類型,也是占用Class文件空間最大的數(shù)據(jù)項目之一强法。
?常量池中主要存放兩大類常量:字面量(Literal)和符號引用(Symbolic References)万俗。
字面量比較接近于Java層面的常量概念,如文本字符串饮怯、被聲明為final的常量值等闰歪。而符號引用總結(jié)起來則包括了下面三類常量:
- 類和接口的全限定名(即帶有包名的Class名,如:com.sunny.common.TestClass)
- 字段的名稱和描述符(private蓖墅、static等描述符)
- 方法的名稱和描述符(private库倘、static等描述符)
?虛擬機在加載Class文件時才會進(jìn)行動態(tài)連接,也就是說论矾,Class文件中不會保存各個方法和字段的最終內(nèi)存布局信息于樟,因此,這些字段和方法的符號引用不經(jīng)過轉(zhuǎn)換是無法直接被虛擬機使用的拇囊。當(dāng)虛擬機運行時迂曲,需要從常量池中獲得對應(yīng)的符號引用,再在類加載過程中的解析階段將其替換為直接引用寥袭,并翻譯到具體的內(nèi)存地址中路捧。
?這里說明下符號引用和直接引用的區(qū)別與關(guān)聯(lián):
-
符號引用
:符號引用以一組符號來描述所引用的目標(biāo)关霸,符號可以是任何形式的字面量,只要使用時能無歧義地定位到目標(biāo)即可杰扫。符號引用與虛擬機實現(xiàn)的內(nèi)存布局無關(guān)队寇,引用的目標(biāo)并不一定已經(jīng)加載到了內(nèi)存中。 -
直接引用
:直接引用可以是直接指向目標(biāo)的指針章姓、相對偏移量或是一個能間接定位到目標(biāo)的句柄佳遣。直接引用是與虛擬機實現(xiàn)的內(nèi)存布局相關(guān)的,同一個符號引用在不同虛擬機實例上翻譯出來的直接引用一般不會相同凡伊。如果有了直接引用零渐,那說明引用的目標(biāo)必定已經(jīng)存在于內(nèi)存之中了。
?常量池中的每一項常量都是一個表系忙,共有14種(JDK1.8)結(jié)構(gòu)各不相同的表結(jié)構(gòu)數(shù)據(jù)诵盼;
1.4.1 常量池通用格式
?所有的常量池項都具有如下通用格式:
cp_info {
u1 tag;
u1 info[];
}
?在常量池表中,每個cp_info項都必須以一個表示cp_info類型的單字節(jié)"tag"項開頭银还。后面info[]數(shù)組的內(nèi)容由tag的值所決定风宁。有效的tag和對應(yīng)的值如下表:
常量類型 | 值 |
---|---|
CONSTANT_Class | 7 |
CONSTANT_Fieldref | 9 |
CONSTANT_Methodref | 10 |
CONSTANT_InterfaceMethodref | 11 |
CONSTANT_String | 8 |
CONSTANT_Integer | 3 |
CONSTANT_Float | 4 |
CONSTANT_Long | 5 |
CONSTANT_Double | 6 |
CONSTANT_NameAndType | 12 |
CONSTANT_Utf8 | 1 |
CONSTANT_MethodHandle | 15 |
CONSTANT_MethodType | 16 |
CONSTANT_InvokeDynamic | 18 |
1.4.2 CONSTANT_Class_info 結(jié)構(gòu)
?CONSTANT_Class_info 結(jié)構(gòu)用于表示類或接口,格式如下:
CONSTANT_Class_info {
u1 tag;
u2 name_index;
}
-
tag
:tag項的值為CONSTANT_Class(7) -
name_index
:name_index項的值必須是堆常量池表的一個有效索引蛹疯。常量池表在該索引處的成員必須是CONSTANT_Utf8_info結(jié)構(gòu)戒财,此結(jié)構(gòu)代表一個有效的類或者接口二進(jìn)制名稱的內(nèi)部形式。
1.4.3 CONSTANT_Fieldref_info捺弦、CONSTANT_Methodref_info和CONSTANT_InterfaceMethodref_info結(jié)構(gòu)
?字段固翰、方法和接口方法由類似的結(jié)構(gòu)表示:
?字段
:
CONSTANT_Fieldref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
?方法
:
CONSTANT_Methodref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
?接口方法
:
CONSTANT_InterfaceMethodref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
?這些結(jié)構(gòu)各項說明如下:
-
tag
:
CONSTANT_Fieldref_info 結(jié)構(gòu)的tag項的值為CONSTANT_Fieldref(9);
CONSTANT_Methodref_info結(jié)構(gòu)的tag項的值為CONSTANT_Methodref(10)
CONSTANT_InterfaceMethodref_info結(jié)構(gòu)的tag項的值為CONSTANT_InterfaceMethodref(11) -
class_index
:
class_index項的值必須是對常量池表的有效索引羹呵,常量池表在該索引處的項必須是CONSTANT_Class_info結(jié)構(gòu)骂际,此結(jié)構(gòu)表示一個類或者接口,當(dāng)前字段或方法時這個類或接口的成員冈欢。
CONSTANT_Methodref_info結(jié)構(gòu)的class_index項歉铝,表示的必須是類(而不能是接口)。
CONSTANT_InterfaceMethodref_info結(jié)構(gòu)的class_index項凑耻,表示的必須是接口類型太示。
CONSTANT_Fieldref_info結(jié)構(gòu)的class_index項既可以表示類也可以表示接口。 -
name_and_type_index
:
name_and_type_index項的值必須是對常量池表的有效索引香浩,常量池表在該索引處的項必須是CONSTANT_NameAndType_info結(jié)構(gòu)类缤,它表示當(dāng)前字段或方法的名字和描述符。
如果一個CONSTANT_Methodref_info結(jié)構(gòu)的方法名以“<”開頭邻吭,那么餐弱,方法名必須是特殊的<init>,即這個方法時實例初始化方法,它的返回類型必須是void膏蚓。
1.4.4 CONSTANT_String_info結(jié)構(gòu)
?CONSTANT_String_info結(jié)構(gòu)用于表示String類型的常量對象瓢谢,其格式如下:
CONSTANT_String_info {
u1 tag;
u2 string_index;
}
-
tag
:CONSTANT_String_info結(jié)構(gòu)的tag項的值為CONSTANT_String(8)。 -
string_index
:string_index項的值必須是對常量池表的有效索引驮瞧,常量池表在該索引處的成員必須是CONSTANT_Utf8_info結(jié)構(gòu)氓扛,此結(jié)構(gòu)表示Unicode碼點序列,這個序列最終會被初始化為一個String對象论笔。
1.4.5 CONSTANT_Integer_info和CONSTANT_Float_info結(jié)構(gòu)
CONSTANT_Integer_info和CONSTANT_Float_info 表示4字節(jié)(int和float)的數(shù)值常量采郎;
CONSTANT_Integer_info {
u1 tag;
u4 bytes;
}
CONSTANT_Float_info {
u1 tag;
u4 bytes;
}
?這些結(jié)構(gòu)說明如下:
-
tag
:
CONSTANT_Integer_info結(jié)構(gòu)的tag項的值是CONSTANT_Integer(3)。
CONSTANT_Float_info結(jié)構(gòu)的tag項的值是CONSTANT_Float(4)狂魔。 -
bytes
:
CONSTANT_Integer_info結(jié)構(gòu)的bytes項表示int常量的值蒜埋,該值按照big-endian的順序存儲(也就是先存儲高位字節(jié))。
CONSTANT_Float_info結(jié)構(gòu)的bytes項按照IEEE754單精度浮點格式來表示float常量的值毅臊,該值按照big-endian的順序存儲(也就是先存儲高位字節(jié))理茎。
1.4.6 CONSTANT_Long_info和CONSTANT_Double_info結(jié)構(gòu)
CONSTANT_Long_info和CONSTANT_Double_info結(jié)構(gòu)表示8字節(jié)(long和double)的數(shù)值常量黑界。
CONSTANT_Long_info {
u1 tag;
u4 high_bytes;
u4 low_bytes;
}
CONSTANT_Double_info {
u1 tag;
u4 high_bytes;
u4 low_bytes;
}
在class文件的常量池表中管嬉,所有的8字節(jié)常量均占兩個表成員(項)的空間。如果一個CONSTANT_Long_info或CONSTANT_Double_info結(jié)構(gòu)的項在常量池表中的索引位n朗鸠,則常量池表中下一個可用項的索引位n+2蚯撩,此時常量池表中索引為n+1的項仍然有效但必須視為不可用。
-
tag
:
CONSTANT_Long_info結(jié)構(gòu)的tag項是CONSTANT_Long(5)烛占。
CONSTANT_Double_info結(jié)構(gòu)的tag項是CONSTANT_Double(6)胎挎。 -
high_bytes
和low_bytes
:
CONSTANT_Long_info結(jié)構(gòu)中的無符號的high_bytes和low_bytes項,用于共同表示long類型的常量忆家;
1.4.7 CONSTANT_NameAndType_info結(jié)構(gòu)
CONSTANT_NameAndType_info結(jié)構(gòu)用于表示字段或方法犹菇,但是和之前的3個結(jié)構(gòu)不同,CONSTANT_NameAndType_info結(jié)構(gòu)沒有指明該字段或方法所屬的類或接口芽卿;
CONSTANT_NameAndType_info {
u1 tag;
u2 name_index; //name_index 項的值必須是對常量池的有效索引揭芍, 常量池在該索引處的項必須是
CONSTANT_Utf8_info結(jié)構(gòu),這個結(jié)構(gòu)要么表示特殊的方法名<init>卸例,要么表示一個有效
的字段或方法的非限定名( Unqualified Name)称杨。
u2 descriptor_index;//descriptor_index 項的值必須是對常量池的有效索引, 常量池在該索引
處的項必須是CONSTANT_Utf8_info結(jié)構(gòu)筷转。
}
-
tag
:
CONSTANT_NameAndType_info結(jié)構(gòu)的tag項的值為CONSTANT_NameAndType(12)姑原。 -
name_index
:
name_index項的值必須是對常量池表的有效索引,常量池表在該索引處的成員必須是CONSTANT_Utf8_info結(jié)構(gòu)呜舒,這個結(jié)構(gòu)要么表示特殊的方法名<init>锭汛,要么表示一個有效的字段或方法的非限定名。 -
descriptor_index
:
descriptor_index項的值必須是對常量池表的有效索引,常量池表在該索引處的成員必須是CONSTANT_Utf8_info結(jié)構(gòu)店乐,這個結(jié)構(gòu)表示一個有效的字段描述符或方法描述符艰躺。
1.4.8 CONSTANT_Utf8_info結(jié)構(gòu)
CONSTANT_Utf8_info用于表示字符常量的值:
CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];
}
-
tag
:
CONSTANT_Utf8_info結(jié)構(gòu)的tag項的值為CONSTANT_Utf8(1) -
length
:
length項的值指明了bytes[]數(shù)組的長度(注意,不能等同于當(dāng)前結(jié)構(gòu)所表示的String對象的長度)眨八。CONSTANT_Utf8_info結(jié)構(gòu)中的內(nèi)容以length屬性來確定長度腺兴,而不以null作為字符串的終止符。 -
bytes[]
:
bytes[]是表示字符串值的byte數(shù)組廉侧,bytes[]中每個成員的byte值都不會是0页响,也不在0xf0~0xff范圍內(nèi)。
1.4.9 CONSTANT_MethodHandle_info結(jié)構(gòu)
CONSTANT_MethodHandle_info結(jié)構(gòu)用于表示方法句柄段誊;
CONSTANT_MethodHandle_info {
u1 tag;
u1 reference_kind;//reference_kind 項的值必須在 1 至 9 之間(包括 1 和 9)闰蚕,它決定了方法句柄的類型。
方法句柄類型的值表示方法句柄的字節(jié)碼行為连舍。
u2 reference_index;//reference_index 項的值必須是對常量池的有效索引没陡。
}
-
tag
:
CONSTANT_MethodHandle_info結(jié)構(gòu)的tag項的值為CONSTANT_MethodHandle(15)。 -
reference_kind
:
reference_kind項的值必須在范圍1~9(包括1和9)之內(nèi)索赏,它表示方法句柄的類型(king)盼玄。方法句柄類型決定句柄的字節(jié)碼行為(bytecode behavior)。 -
reference_index
:
reference_index項的值必須是對常量池表的有效索引潜腻;
1.4.10 CONSTANT_MethodType_info結(jié)構(gòu)
CONSTANT_MethodType_info結(jié)構(gòu)表示方法類型:
CONSTANT_MethodType_info {
u1 tag;
u2 descriptor_index;
}
-
tag
:
CONSTANT_MethodType_info結(jié)構(gòu)的tag項的值為CONSTANT_MethodType(16)埃儿。 -
descriptor_index
:
descriptor_index項的值必須是對常量池表的有效索引,常量池表在該索引處的成員必須是CONSTANT_Utf8_info結(jié)構(gòu)融涣,這個結(jié)構(gòu)表示一個有效方法描述符童番。
1.4.11 CONSTANT_InvokeDynamic_info結(jié)構(gòu)
CONSTANT_InvokeDynamic_info結(jié)構(gòu)用于表示invokedynamic指令所用到的引導(dǎo)方法(bootstrap method)、引導(dǎo)方法所用到的動態(tài)調(diào)用名稱(dynamic invocation name)威鹿、參數(shù)和返回類型剃斧,并可以給引導(dǎo)方法傳入一系列稱為靜態(tài)參數(shù)(static argument)的常量。
CONSTANT_InvokeDynamic_info {
u1 tag;
u2 bootstrap_method_attr_index;
u2 name_and_type_index;
}
-
tag
:
CONSTANT_InvokeDynamic_info結(jié)構(gòu)的tag項的值為CONSTANT_InvokeDynamic(18)忽你。 -
bootstrap_method_attr_index
:
bootstrap_method_attr_index項的值必須是對當(dāng)前class文件中引導(dǎo)方法表的bootstrap_methods數(shù)組的有效索引幼东。 -
name_and_type_index
:
name_and_type_index項的值必須是對常量池表的有效索引,常量池表在該索引處的成員必須是CONSTANT_NameAndType_info結(jié)構(gòu)檀夹,此結(jié)構(gòu)表示方法名和方法描述符筋粗。
1.5 訪問標(biāo)識(access_flag)
?在常量池結(jié)束之后,緊接著的兩個字節(jié)代表訪問標(biāo)志(access_flags)炸渡,這個標(biāo)志用于識別一些類或者接口層次的訪問信息娜亿,包括:這個Class是類還是接口;是否定義為public類型蚌堵;是否定義為abstract類型买决,如果是類的話沛婴,是否被聲明為final等,具體的標(biāo)志位以及標(biāo)志的含義如下:
標(biāo)記名 | 值 | 含義 |
---|---|---|
ACC_PUBLIC | 0x0001 | 可以被包的類外訪問督赤。 |
ACC_FINAL | 0x0010 | 不允許有子類嘁灯。 |
ACC_SUPER | 0x0020 | 當(dāng)用到 invokespecial 指令時,需要特殊處理的父類方法躲舌。 |
ACC_INTERFACE | 0x0200 | 標(biāo)識定義的是接口而不是類丑婿。 |
ACC_ABSTRACT | 0x0400 | 不能被實例化。 |
ACC_SYNTHETIC | 0x1000 | 標(biāo)識并非 Java 源碼生成的代碼没卸。 |
ACC_ANNOTATION | 0x2000 | 標(biāo)識注解類型 |
ACC_ENUM | 0x4000 | 標(biāo)識枚舉類型 |
1.6 類索引羹奉、父類索引與接口索引集合
?類索引(this_class)和父類索引(super_class)都是一個u2類型的數(shù)據(jù),而接口索引集合(interfaces)是一組u2類型的數(shù)據(jù)的集合约计,Class文件中由這三項數(shù)據(jù)來確定這個類的繼承關(guān)系诀拭。類索引用于確定這個類的全限定名,父類索引用于確定這個類的父類的全限定名煤蚌。Java不允許多重繼承耕挨,所以父類索引只有一個,除了java.lang.Object外尉桩,所有Java類的父類索引都不為0筒占。接口索引集合就用來描述這個類實現(xiàn)了哪些接口,所有被實現(xiàn)的接口按類定義中的implements(如果類是一個接口則是extends)后的接口順序從左到右排列在接口的索引集合中魄健。
1.7 字段表集合(field_info)
?字段表(field_info)用于描述接口或類中聲明的變量赋铝。字段(field)包括了類級變量和實例級變量插勤,但不包括方法內(nèi)部聲明的變量沽瘦。一個字段的信息包括:作用域(public、private农尖、protected修飾符)析恋、是實例變量還是類變量(static修飾符)、可變性(final)盛卡、并發(fā)可見性(volatile修飾符助隧,是否強制從主內(nèi)存讀寫)、可否序列化(transient修飾符)滑沧、字段數(shù)據(jù)類型(基本數(shù)據(jù)類型并村、對象、數(shù)組)滓技、字段名稱哩牍。這些信息中,各個修飾符都是布爾值令漂,要么有膝昆,要么沒有丸边。
- 字段結(jié)構(gòu)如下:
field_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
- 字段 access_flags 標(biāo)記列表及其含義:
標(biāo)記名 | 值 | 說明 |
---|---|---|
ACC_PUBLIC | 0x0001 | public,表示字段可以從任何包訪問荚孵。 |
ACC_PRIVATE | 0x0002 | private妹窖,表示字段僅能該類自身調(diào)用。 |
ACC_PROTECTED | 0x0004 | protected收叶,表示字段可以被子類調(diào)用骄呼。 |
ACC_STATIC | 0x0008 | static,表示靜態(tài)字段判没。 |
ACC_FINAL | 0x0010 | final谒麦,表示字段定義后值無法修改 |
ACC_VOLATILE | 0x0040 | volatile,表示字段是易變的哆致。 |
ACC_TRANSIENT | 0x0080 | transient绕德,表示字段不會被序列化 |
ACC_SYNTHETIC | 0x1000 | 表示字段由編譯器自動產(chǎn)生。 |
ACC_ENUM | 0x4000 | enum摊阀,表示字段為枚舉類型 |
?全限定名稱:如果TestClass類是定義在com.sunny.common.TestClass包中耻蛇,那么這個類的全限定名為com/sunny/common/TestClass。
?簡單名稱:簡單名稱指沒有類型和參數(shù)修飾的方法或字段名稱胞此,在上面的例子中臣咖,TestClass類中的inc()方法和num字段的簡單名稱分別為“inc”和“num”。
?描述符:描述符的作用是用來描述字段的數(shù)據(jù)類型漱牵、方法的參數(shù)列表(包括數(shù)量夺蛇、類型以及順序)和返回值。根據(jù)描述符規(guī)則酣胀,基本數(shù)據(jù)類型(byte,char,double,float,int,long,short,boolean)及代表無返回值的void類型都用一個大寫字符來表示刁赦,而對象則用字符L加對象的全限定名來表示,如下所示:
字符 | 類型 | 含義 |
---|---|---|
B | byte | 有符號字節(jié)型數(shù) |
C | char | Unicode 字符闻镶, UTF-16 編碼 |
D | double | 雙精度浮點數(shù) |
F | float | 單精度浮點數(shù) |
I | int | 整型數(shù) |
J | long | 長整數(shù) |
S | short | 有符號短整數(shù) |
Z | boolean | 布爾值 true/false |
L Classname; | reference | 一個名為Classname的實例 |
[ | reference | 一個一維數(shù)組 |
?對于數(shù)組類型甚脉,每一個維度用一個前置的“[”字符來描述,如定義個int[][]類型的二維數(shù)組铆农,記錄為:"[[I"牺氨。
?用描述符來描述方法時,按照先參數(shù)列表后返回值的順序描述墩剖。參數(shù)裂變按照參數(shù)順序放在“()”內(nèi)猴凹,如方法void login()描述符為“()V”,方法java.lang.String toString()的描述符為“()Ljava.lang.String”岭皂。
1.8 方法表集合(method_info)
?方法表(method_info)的結(jié)構(gòu)與屬性表的結(jié)構(gòu)相同郊霎,不過多贅述。方法里的Java代碼蒲障,經(jīng)過編譯器編譯成字節(jié)碼指令后歹篓,存放在方法屬性表集合中一個名為“Code”的屬性里瘫证,關(guān)于屬性表的項目,同樣會在后面詳細(xì)介紹庄撮。
?與字段表集合相對應(yīng)背捌,如果父類方法在子類中沒有被覆寫,方法表集合中就不會出現(xiàn)來自父類的方法信息洞斯。但同樣毡庆,有可能會出現(xiàn)由編譯器自動添加的方法,最典型的便是類構(gòu)造器“<clinit>”方法和實例構(gòu)造器“<init>”方法烙如。
? 在Java語言中么抗,要重載一個方法,除了要與原方法具有相同的簡單名稱外亚铁,還要求必須擁有一個與原方法不同的特征簽名蝇刀,特征簽名就是一個方法中各個參數(shù)在常量池中的字段符號引用的集合,也就是因為返回值不會包含在特征簽名之中徘溢,因此Java語言里無法僅僅依靠返回值的不同來對一個已有方法進(jìn)行重載吞琐。
? method_info 結(jié)構(gòu)格式如下:
method_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
方法 access_flags 標(biāo)記列表及其含義:
標(biāo)記名 | 值 | 說明 |
---|---|---|
ACC_PUBLIC | 0x0001 | public,方法可以從包外訪問 |
ACC_PRIVATE | 0x0002 | private然爆,方法只能本類中訪問 |
ACC_PROTECTED | 0x0004 | protected站粟,方法在自身和子類可以訪問 |
ACC_STATIC | 0x0008 | static,靜態(tài)方法 |
ACC_FINAL | 0x0010 | final曾雕,方法不能被重寫(覆蓋) |
ACC_SYNCHRONIZED | 0x0020 | synchronized奴烙,方法由管程同步 |
ACC_BRIDGE | 0x0040 | bridge,方法由編譯器產(chǎn)生 |
ACC_VARARGS | 0x0080 | 表示方法帶有變長參數(shù) |
ACC_NATIVE | 0x0100 | native剖张,方法引用非 java 語言的本地方法 |
ACC_ABSTRACT | 0x0400 | abstract切诀,方法沒有具體實現(xiàn) |
ACC_STRICT | 0x0800 | strictfp,方法使用 FP-strict 浮點格式 |
ACC_SYNTHETIC | 0x1000 | 方法在源文件中不出現(xiàn)修械,由編譯器產(chǎn)生 |
1.9 屬性表(attribute_info)
?屬性表(attribute_info)在前面已經(jīng)出現(xiàn)過多系趾牧,在Class文件检盼、字段表肯污、方法表中都可以攜帶自己的屬性表集合,以用于描述某些場景專有的信息吨枉。
?屬性表集合的限制沒有那么嚴(yán)格蹦渣,不再要求各個屬性表具有嚴(yán)格的順序,并且只要不與已有的屬性名重復(fù)貌亭,任何人實現(xiàn)的編譯器都可以向?qū)傩员碇袑懭胱约憾x的屬性信息柬唯,但Java虛擬機運行時會忽略掉它不認(rèn)識的屬性。關(guān)于虛擬機規(guī)范中預(yù)定義的屬性圃庭,這里不展開講了锄奢,列舉幾個常用的失晴。
1.9.1 屬性的通用格式
attribute_info {
u2 attribute_name_index; //屬性名索引
u4 attribute_length; //屬性長度
u1 info[attribute_length]; //屬性的具體內(nèi)容
}
1.9.2 ConstantValue 屬性
?ConstantValue 屬性表示一個常量字段的值。位于 field_info結(jié)構(gòu)的屬性表中拘央。
ConstantValue_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 constantvalue_index;//字段值在常量池中的索引涂屁,常量池在該索引處的項給出該屬性表示的常量值。(例如灰伟,值是long型的拆又,在常量池中便是CONSTANT_Long)
}
1.9.3 Deprecated 屬性
?Deprecated 屬性是在 JDK 1.1 為了支持注釋中的關(guān)鍵詞@deprecated 而引入的。
Deprecated_attribute {
u2 attribute_name_index;
u4 attribute_length;
}
1.9.4Code 屬性
Code_attribute {
u2 attribute_name_index; //常量池中的uft8類型的索引栏账,值固定為”Code“
u4 attribute_length; //屬性值長度帖族,為整個屬性表長度-6
u2 max_stack; //操作數(shù)棧的最大深度值,jvm運行時根據(jù)該值分配棧幀
u2 max_locals; //局部變量表最大存儲空間挡爵,單位是slot
u4 code_length; // 字節(jié)碼指令的個數(shù)
u1 code[code_length]; // 具體的字節(jié)碼指令
u2 exception_table_length; //異常的個數(shù)
{ u2 start_pc;
u2 end_pc;
u2 handler_pc; //當(dāng)字節(jié)碼在[start_pc, end_pc)區(qū)間出現(xiàn)catch_type或子類竖般,則轉(zhuǎn)到handler_pc行繼續(xù)處理。
u2 catch_type; //當(dāng)catch_type=0茶鹃,則任意異常都需轉(zhuǎn)到handler_pc處理
} exception_table[exception_table_length]; //具體的異常內(nèi)容
u2 attributes_count; //屬性的個數(shù)
attribute_info attributes[attributes_count]; //具體的屬性內(nèi)容
}
- 其中slot為局部變量中的最小單位捻激。boolean、 byte前计、 char胞谭、 short、 float男杈、 reference和 returnAddress 等小于等于32位的用一個slot表示丈屹,double,long這些大于32位的用2個slot表示。
1.9.5 InnerClasses 屬性
?為了方便說明特別定義一個表示類或接口的 Class 格式為 C伶棒。如果 C 的常量池中包含某個CONSTANT_Class_info 成員旺垒,且這個成員所表示的類或接口不屬于任何一個包,那么 C 的ClassFile 結(jié)構(gòu)的屬性表中就必須含有對應(yīng)的 InnerClasses 屬性肤无。InnerClasses 屬性是在 JDK 1.1 中為了支持內(nèi)部類和內(nèi)部接口而引入的,位于 ClassFile結(jié)構(gòu)的屬性表先蒋。
InnerClasses_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 number_of_classes;
{ u2 inner_class_info_index;
u2 outer_class_info_index;
u2 inner_name_index;
u2 inner_class_access_flags;
} classes[number_of_classes];
}
1.9.6 LineNumberTable 屬性
?LineNumberTable 屬性是可選變長屬性,位于 Code結(jié)構(gòu)的屬性表宛渐。它被調(diào)試器用于確定源文件中行號表示的內(nèi)容在 Java 虛擬機的 code[]數(shù)組中對應(yīng)的部分竞漾。在 Code 屬性的屬性表中,LineNumberTable 屬性可以按照任意順序出現(xiàn),此外窥翩,多個 LineNumberTable屬性可以共同表示一個行號在源文件中表示的內(nèi)容业岁,即 LineNumberTable 屬性不需要與源文件的行一一對應(yīng)。
LineNumberTable_attribute {
u2 attribute_name_index;//屬性名稱在常量池的索引寇蚊,指向一個 CONSTANT_Utf8_info結(jié)構(gòu)笔时。
u4 attribute_length;//屬性長度
u2 line_number_table_length;//線性表長度
{ u2 start_pc;
u2 line_number;
} line_number_table[line_number_table_length];
}
1.9.7 LocalVariableTable 屬性
?LocalVariableTable 是可選變長屬性,位于 Code屬性的屬性表中仗岸。它被調(diào)試器用于確定方法在執(zhí)行過程中局部變量的信息允耿。在 Code 屬性的屬性表中借笙,LocalVariableTable 屬性可以按照任意順序出現(xiàn)。 Code 屬性中的每個局部變量最多只能有一
個 LocalVariableTable 屬性较锡。
LocalVariableTable_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 local_variable_table_length
{ u2 start_pc;
u2 length;
u2 name_index;
u2 descriptor_index;
u2 index;
} local_variable_table[local_variable_table_length];
}
1.9.8 Signature 屬性
?Signature 屬性是可選的定長屬性提澎,位于 ClassFile, field_info
或 method_info結(jié)構(gòu)的屬性表中念链。在 Java 語言中盼忌,任何類、 接口掂墓、 初始化方法或成員的泛型簽名如果包含了類型變量( Type Variables) 或參數(shù)化類型( Parameterized Types)谦纱,則 Signature 屬性會為它記錄泛型簽名信息。
Signature_attribute {
u2 attribute_name_index;//屬性名稱在常量池中的索引君编,指向一個 CONSTANT_Utf8_info結(jié)構(gòu)跨嘉。
u4 attribute_length;
u2 signature_index;
}
- slot是虛擬機未局部變量分配內(nèi)存使用的最小單位。對于byte/char/float/int/short/boolean/returnAddress等長度不超過32位的局部變量吃嘿,每個占用1個Slot祠乃;對于long和double這兩種64位的數(shù)據(jù)類型則需要2個Slot來存放。
- 實例方法中有隱藏參數(shù)this, 顯式異常處理器的參數(shù)兑燥,方法體定義的局部變量都使用局部變量表來存放亮瓷。
- max_locals,不是所有局部變量所占Slot之和降瞳,因為Slot可以重用嘱支,javac編譯器會根據(jù)變量的作用域來分配Slot給各個變量使用,從而計算出max_locals大小挣饥。
- 虛擬機規(guī)范限制嚴(yán)格方法不允許超過65535個字節(jié)碼除师,否則拒絕編譯。
參考文獻(xiàn):
《Java虛擬機規(guī)范 Java SE 8版》
周志明:《深入理解Java虛擬機:JVM高級特性與最佳實踐(最新第二版)》