?每一個class文件都對應著唯一一個類或者接口的定義信息,但是相對地,類或者接口并不一定都必須定義在文件里(比如類或者接口也可以通過類加載器直接生成)。我們通俗地將任意一個有效的類或者接口所應當滿足的格式稱為“class文件格式”,即使它不一定以磁盤文件的形式存在。
?Class文件是有8個字節為基礎的字節流構成的,這些字節流之間都嚴格按照規定的順序排列,并且字節之間不存在任何空隙,對于超過8個字節的數據,將按 照Big-Endian的順序存儲,也就是說高位字節存儲在低的地址上面,而低位字節存儲到高地址上面,其實這也是class文件要跨平臺的關鍵,因為 PowerPC架構的處理采用Big-Endian的存儲順序,而x86系列的處理器則采用Little-Endian的存儲順序,因此為了Class文 件在各中處理器架構下保持統一的存儲順序,虛擬機規范必須對起進行統一。在Java JDK中,可以使用java.io.DataInput、java.io.DataOutput等接口和java.io.DataInputStream和java.io.DataOutputStream等類來訪問這種格式的數據。
?Class文件結構采用類似C語言的結構體來存儲數據的,主要有兩類數據項,無符號數和表,無符號數用來表述數字,索引引用以及字符串等,比如 u1,u2,u4,u8分別代表1個字節,2個字節,4個字節,8個字節的無符號數,而表是有多個無符號數以及其它的表組成的復合結構。
一、class的文件結構
類型 | 名稱 | 數量 | |
---|---|---|---|
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 魔數(u4 magic)
?每個Class文件的頭4個字節稱為魔數(magic),它的唯一作用是判斷該文件是否為一個能被虛擬機接受的Class文件。它的值固定為0xCAFEBABE。
1.2 class文件版本
u2 minor_version
:副版本號
u2 major_version
:主版本號
?主副版本號共同構成了 Class 文件的格式版本號。譬如某個 Class 文件的主版本號為 M,副版本號為 m,那么這個Class 文件的格式版本號就確定為 M.m。一個 Java 虛擬機實例只能支持特定范圍內的主版本號。不同版本的Java編譯器編譯的Class文件對應的版本是不一樣的。高版本的虛擬機支持低版本的編譯器編譯的 Class文件結構。比如Java SE 6.0對應的虛擬機支持Java SE 5.0的編譯器編譯的Class文件結構,反之則不行。
1.3 常量池計數器(constant_pool_count )
?常量池計數器,constant_pool_count 的值等于 constant_pool 表中的成員數加 1。constant_pool 表的索引值只有在大于 0 且小于 constant_pool_count 時才會被認為是有效的。(0表示不引用常量池的任一項)
1.4 常量池(constant_pool)
?major_version之后是常量池(constant_pool)的入口,它是Class文件中與其他項目關聯最多的數據類型,也是占用Class文件空間最大的數據項目之一。
?常量池中主要存放兩大類常量:字面量(Literal)和符號引用(Symbolic References)。
字面量比較接近于Java層面的常量概念,如文本字符串、被聲明為final的常量值等。而符號引用總結起來則包括了下面三類常量:
- 類和接口的全限定名(即帶有包名的Class名,如:com.sunny.common.TestClass)
- 字段的名稱和描述符(private、static等描述符)
- 方法的名稱和描述符(private、static等描述符)
?虛擬機在加載Class文件時才會進行動態連接,也就是說,Class文件中不會保存各個方法和字段的最終內存布局信息,因此,這些字段和方法的符號引用不經過轉換是無法直接被虛擬機使用的。當虛擬機運行時,需要從常量池中獲得對應的符號引用,再在類加載過程中的解析階段將其替換為直接引用,并翻譯到具體的內存地址中。
?這里說明下符號引用和直接引用的區別與關聯:
-
符號引用
:符號引用以一組符號來描述所引用的目標,符號可以是任何形式的字面量,只要使用時能無歧義地定位到目標即可。符號引用與虛擬機實現的內存布局無關,引用的目標并不一定已經加載到了內存中。 -
直接引用
:直接引用可以是直接指向目標的指針、相對偏移量或是一個能間接定位到目標的句柄。直接引用是與虛擬機實現的內存布局相關的,同一個符號引用在不同虛擬機實例上翻譯出來的直接引用一般不會相同。如果有了直接引用,那說明引用的目標必定已經存在于內存之中了。
?常量池中的每一項常量都是一個表,共有14種(JDK1.8)結構各不相同的表結構數據;
1.4.1 常量池通用格式
?所有的常量池項都具有如下通用格式:
cp_info {
u1 tag;
u1 info[];
}
?在常量池表中,每個cp_info項都必須以一個表示cp_info類型的單字節"tag"項開頭。后面info[]數組的內容由tag的值所決定。有效的tag和對應的值如下表:
常量類型 | 值 |
---|---|
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 結構
?CONSTANT_Class_info 結構用于表示類或接口,格式如下:
CONSTANT_Class_info {
u1 tag;
u2 name_index;
}
-
tag
:tag項的值為CONSTANT_Class(7) -
name_index
:name_index項的值必須是堆常量池表的一個有效索引。常量池表在該索引處的成員必須是CONSTANT_Utf8_info結構,此結構代表一個有效的類或者接口二進制名稱的內部形式。
1.4.3 CONSTANT_Fieldref_info、CONSTANT_Methodref_info和CONSTANT_InterfaceMethodref_info結構
?字段、方法和接口方法由類似的結構表示:
?字段
:
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;
}
?這些結構各項說明如下:
-
tag
:
CONSTANT_Fieldref_info 結構的tag項的值為CONSTANT_Fieldref(9);
CONSTANT_Methodref_info結構的tag項的值為CONSTANT_Methodref(10)
CONSTANT_InterfaceMethodref_info結構的tag項的值為CONSTANT_InterfaceMethodref(11) -
class_index
:
class_index項的值必須是對常量池表的有效索引,常量池表在該索引處的項必須是CONSTANT_Class_info結構,此結構表示一個類或者接口,當前字段或方法時這個類或接口的成員。
CONSTANT_Methodref_info結構的class_index項,表示的必須是類(而不能是接口)。
CONSTANT_InterfaceMethodref_info結構的class_index項,表示的必須是接口類型。
CONSTANT_Fieldref_info結構的class_index項既可以表示類也可以表示接口。 -
name_and_type_index
:
name_and_type_index項的值必須是對常量池表的有效索引,常量池表在該索引處的項必須是CONSTANT_NameAndType_info結構,它表示當前字段或方法的名字和描述符。
如果一個CONSTANT_Methodref_info結構的方法名以“<”開頭,那么,方法名必須是特殊的<init>,即這個方法時實例初始化方法,它的返回類型必須是void。
1.4.4 CONSTANT_String_info結構
?CONSTANT_String_info結構用于表示String類型的常量對象,其格式如下:
CONSTANT_String_info {
u1 tag;
u2 string_index;
}
-
tag
:CONSTANT_String_info結構的tag項的值為CONSTANT_String(8)。 -
string_index
:string_index項的值必須是對常量池表的有效索引,常量池表在該索引處的成員必須是CONSTANT_Utf8_info結構,此結構表示Unicode碼點序列,這個序列最終會被初始化為一個String對象。
1.4.5 CONSTANT_Integer_info和CONSTANT_Float_info結構
CONSTANT_Integer_info和CONSTANT_Float_info 表示4字節(int和float)的數值常量;
CONSTANT_Integer_info {
u1 tag;
u4 bytes;
}
CONSTANT_Float_info {
u1 tag;
u4 bytes;
}
?這些結構說明如下:
-
tag
:
CONSTANT_Integer_info結構的tag項的值是CONSTANT_Integer(3)。
CONSTANT_Float_info結構的tag項的值是CONSTANT_Float(4)。 -
bytes
:
CONSTANT_Integer_info結構的bytes項表示int常量的值,該值按照big-endian的順序存儲(也就是先存儲高位字節)。
CONSTANT_Float_info結構的bytes項按照IEEE754單精度浮點格式來表示float常量的值,該值按照big-endian的順序存儲(也就是先存儲高位字節)。
1.4.6 CONSTANT_Long_info和CONSTANT_Double_info結構
CONSTANT_Long_info和CONSTANT_Double_info結構表示8字節(long和double)的數值常量。
CONSTANT_Long_info {
u1 tag;
u4 high_bytes;
u4 low_bytes;
}
CONSTANT_Double_info {
u1 tag;
u4 high_bytes;
u4 low_bytes;
}
在class文件的常量池表中,所有的8字節常量均占兩個表成員(項)的空間。如果一個CONSTANT_Long_info或CONSTANT_Double_info結構的項在常量池表中的索引位n,則常量池表中下一個可用項的索引位n+2,此時常量池表中索引為n+1的項仍然有效但必須視為不可用。
-
tag
:
CONSTANT_Long_info結構的tag項是CONSTANT_Long(5)。
CONSTANT_Double_info結構的tag項是CONSTANT_Double(6)。 -
high_bytes
和low_bytes
:
CONSTANT_Long_info結構中的無符號的high_bytes和low_bytes項,用于共同表示long類型的常量;
1.4.7 CONSTANT_NameAndType_info結構
CONSTANT_NameAndType_info結構用于表示字段或方法,但是和之前的3個結構不同,CONSTANT_NameAndType_info結構沒有指明該字段或方法所屬的類或接口;
CONSTANT_NameAndType_info {
u1 tag;
u2 name_index; //name_index 項的值必須是對常量池的有效索引, 常量池在該索引處的項必須是
CONSTANT_Utf8_info結構,這個結構要么表示特殊的方法名<init>,要么表示一個有效
的字段或方法的非限定名( Unqualified Name)。
u2 descriptor_index;//descriptor_index 項的值必須是對常量池的有效索引, 常量池在該索引
處的項必須是CONSTANT_Utf8_info結構。
}
-
tag
:
CONSTANT_NameAndType_info結構的tag項的值為CONSTANT_NameAndType(12)。 -
name_index
:
name_index項的值必須是對常量池表的有效索引,常量池表在該索引處的成員必須是CONSTANT_Utf8_info結構,這個結構要么表示特殊的方法名<init>,要么表示一個有效的字段或方法的非限定名。 -
descriptor_index
:
descriptor_index項的值必須是對常量池表的有效索引,常量池表在該索引處的成員必須是CONSTANT_Utf8_info結構,這個結構表示一個有效的字段描述符或方法描述符。
1.4.8 CONSTANT_Utf8_info結構
CONSTANT_Utf8_info用于表示字符常量的值:
CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];
}
-
tag
:
CONSTANT_Utf8_info結構的tag項的值為CONSTANT_Utf8(1) -
length
:
length項的值指明了bytes[]數組的長度(注意,不能等同于當前結構所表示的String對象的長度)。CONSTANT_Utf8_info結構中的內容以length屬性來確定長度,而不以null作為字符串的終止符。 -
bytes[]
:
bytes[]是表示字符串值的byte數組,bytes[]中每個成員的byte值都不會是0,也不在0xf0~0xff范圍內。
1.4.9 CONSTANT_MethodHandle_info結構
CONSTANT_MethodHandle_info結構用于表示方法句柄;
CONSTANT_MethodHandle_info {
u1 tag;
u1 reference_kind;//reference_kind 項的值必須在 1 至 9 之間(包括 1 和 9),它決定了方法句柄的類型。
方法句柄類型的值表示方法句柄的字節碼行為。
u2 reference_index;//reference_index 項的值必須是對常量池的有效索引。
}
-
tag
:
CONSTANT_MethodHandle_info結構的tag項的值為CONSTANT_MethodHandle(15)。 -
reference_kind
:
reference_kind項的值必須在范圍1~9(包括1和9)之內,它表示方法句柄的類型(king)。方法句柄類型決定句柄的字節碼行為(bytecode behavior)。 -
reference_index
:
reference_index項的值必須是對常量池表的有效索引;
1.4.10 CONSTANT_MethodType_info結構
CONSTANT_MethodType_info結構表示方法類型:
CONSTANT_MethodType_info {
u1 tag;
u2 descriptor_index;
}
-
tag
:
CONSTANT_MethodType_info結構的tag項的值為CONSTANT_MethodType(16)。 -
descriptor_index
:
descriptor_index項的值必須是對常量池表的有效索引,常量池表在該索引處的成員必須是CONSTANT_Utf8_info結構,這個結構表示一個有效方法描述符。
1.4.11 CONSTANT_InvokeDynamic_info結構
CONSTANT_InvokeDynamic_info結構用于表示invokedynamic指令所用到的引導方法(bootstrap method)、引導方法所用到的動態調用名稱(dynamic invocation name)、參數和返回類型,并可以給引導方法傳入一系列稱為靜態參數(static argument)的常量。
CONSTANT_InvokeDynamic_info {
u1 tag;
u2 bootstrap_method_attr_index;
u2 name_and_type_index;
}
-
tag
:
CONSTANT_InvokeDynamic_info結構的tag項的值為CONSTANT_InvokeDynamic(18)。 -
bootstrap_method_attr_index
:
bootstrap_method_attr_index項的值必須是對當前class文件中引導方法表的bootstrap_methods數組的有效索引。 -
name_and_type_index
:
name_and_type_index項的值必須是對常量池表的有效索引,常量池表在該索引處的成員必須是CONSTANT_NameAndType_info結構,此結構表示方法名和方法描述符。
1.5 訪問標識(access_flag)
?在常量池結束之后,緊接著的兩個字節代表訪問標志(access_flags),這個標志用于識別一些類或者接口層次的訪問信息,包括:這個Class是類還是接口;是否定義為public類型;是否定義為abstract類型,如果是類的話,是否被聲明為final等,具體的標志位以及標志的含義如下:
標記名 | 值 | 含義 |
---|---|---|
ACC_PUBLIC | 0x0001 | 可以被包的類外訪問。 |
ACC_FINAL | 0x0010 | 不允許有子類。 |
ACC_SUPER | 0x0020 | 當用到 invokespecial 指令時,需要特殊處理的父類方法。 |
ACC_INTERFACE | 0x0200 | 標識定義的是接口而不是類。 |
ACC_ABSTRACT | 0x0400 | 不能被實例化。 |
ACC_SYNTHETIC | 0x1000 | 標識并非 Java 源碼生成的代碼。 |
ACC_ANNOTATION | 0x2000 | 標識注解類型 |
ACC_ENUM | 0x4000 | 標識枚舉類型 |
1.6 類索引、父類索引與接口索引集合
?類索引(this_class)和父類索引(super_class)都是一個u2類型的數據,而接口索引集合(interfaces)是一組u2類型的數據的集合,Class文件中由這三項數據來確定這個類的繼承關系。類索引用于確定這個類的全限定名,父類索引用于確定這個類的父類的全限定名。Java不允許多重繼承,所以父類索引只有一個,除了java.lang.Object外,所有Java類的父類索引都不為0。接口索引集合就用來描述這個類實現了哪些接口,所有被實現的接口按類定義中的implements(如果類是一個接口則是extends)后的接口順序從左到右排列在接口的索引集合中。
1.7 字段表集合(field_info)
?字段表(field_info)用于描述接口或類中聲明的變量。字段(field)包括了類級變量和實例級變量,但不包括方法內部聲明的變量。一個字段的信息包括:作用域(public、private、protected修飾符)、是實例變量還是類變量(static修飾符)、可變性(final)、并發可見性(volatile修飾符,是否強制從主內存讀寫)、可否序列化(transient修飾符)、字段數據類型(基本數據類型、對象、數組)、字段名稱。這些信息中,各個修飾符都是布爾值,要么有,要么沒有。
- 字段結構如下:
field_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
- 字段 access_flags 標記列表及其含義:
標記名 | 值 | 說明 |
---|---|---|
ACC_PUBLIC | 0x0001 | public,表示字段可以從任何包訪問。 |
ACC_PRIVATE | 0x0002 | private,表示字段僅能該類自身調用。 |
ACC_PROTECTED | 0x0004 | protected,表示字段可以被子類調用。 |
ACC_STATIC | 0x0008 | static,表示靜態字段。 |
ACC_FINAL | 0x0010 | final,表示字段定義后值無法修改 |
ACC_VOLATILE | 0x0040 | volatile,表示字段是易變的。 |
ACC_TRANSIENT | 0x0080 | transient,表示字段不會被序列化 |
ACC_SYNTHETIC | 0x1000 | 表示字段由編譯器自動產生。 |
ACC_ENUM | 0x4000 | enum,表示字段為枚舉類型 |
?全限定名稱:如果TestClass類是定義在com.sunny.common.TestClass包中,那么這個類的全限定名為com/sunny/common/TestClass。
?簡單名稱:簡單名稱指沒有類型和參數修飾的方法或字段名稱,在上面的例子中,TestClass類中的inc()方法和num字段的簡單名稱分別為“inc”和“num”。
?描述符:描述符的作用是用來描述字段的數據類型、方法的參數列表(包括數量、類型以及順序)和返回值。根據描述符規則,基本數據類型(byte,char,double,float,int,long,short,boolean)及代表無返回值的void類型都用一個大寫字符來表示,而對象則用字符L加對象的全限定名來表示,如下所示:
字符 | 類型 | 含義 |
---|---|---|
B | byte | 有符號字節型數 |
C | char | Unicode 字符, UTF-16 編碼 |
D | double | 雙精度浮點數 |
F | float | 單精度浮點數 |
I | int | 整型數 |
J | long | 長整數 |
S | short | 有符號短整數 |
Z | boolean | 布爾值 true/false |
L Classname; | reference | 一個名為Classname的實例 |
[ | reference | 一個一維數組 |
?對于數組類型,每一個維度用一個前置的“[”字符來描述,如定義個int[][]類型的二維數組,記錄為:"[[I"。
?用描述符來描述方法時,按照先參數列表后返回值的順序描述。參數裂變按照參數順序放在“()”內,如方法void login()描述符為“()V”,方法java.lang.String toString()的描述符為“()Ljava.lang.String”。
1.8 方法表集合(method_info)
?方法表(method_info)的結構與屬性表的結構相同,不過多贅述。方法里的Java代碼,經過編譯器編譯成字節碼指令后,存放在方法屬性表集合中一個名為“Code”的屬性里,關于屬性表的項目,同樣會在后面詳細介紹。
?與字段表集合相對應,如果父類方法在子類中沒有被覆寫,方法表集合中就不會出現來自父類的方法信息。但同樣,有可能會出現由編譯器自動添加的方法,最典型的便是類構造器“<clinit>”方法和實例構造器“<init>”方法。
? 在Java語言中,要重載一個方法,除了要與原方法具有相同的簡單名稱外,還要求必須擁有一個與原方法不同的特征簽名,特征簽名就是一個方法中各個參數在常量池中的字段符號引用的集合,也就是因為返回值不會包含在特征簽名之中,因此Java語言里無法僅僅依靠返回值的不同來對一個已有方法進行重載。
? method_info 結構格式如下:
method_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
方法 access_flags 標記列表及其含義:
標記名 | 值 | 說明 |
---|---|---|
ACC_PUBLIC | 0x0001 | public,方法可以從包外訪問 |
ACC_PRIVATE | 0x0002 | private,方法只能本類中訪問 |
ACC_PROTECTED | 0x0004 | protected,方法在自身和子類可以訪問 |
ACC_STATIC | 0x0008 | static,靜態方法 |
ACC_FINAL | 0x0010 | final,方法不能被重寫(覆蓋) |
ACC_SYNCHRONIZED | 0x0020 | synchronized,方法由管程同步 |
ACC_BRIDGE | 0x0040 | bridge,方法由編譯器產生 |
ACC_VARARGS | 0x0080 | 表示方法帶有變長參數 |
ACC_NATIVE | 0x0100 | native,方法引用非 java 語言的本地方法 |
ACC_ABSTRACT | 0x0400 | abstract,方法沒有具體實現 |
ACC_STRICT | 0x0800 | strictfp,方法使用 FP-strict 浮點格式 |
ACC_SYNTHETIC | 0x1000 | 方法在源文件中不出現,由編譯器產生 |
1.9 屬性表(attribute_info)
?屬性表(attribute_info)在前面已經出現過多系,在Class文件、字段表、方法表中都可以攜帶自己的屬性表集合,以用于描述某些場景專有的信息。
?屬性表集合的限制沒有那么嚴格,不再要求各個屬性表具有嚴格的順序,并且只要不與已有的屬性名重復,任何人實現的編譯器都可以向屬性表中寫入自己定義的屬性信息,但Java虛擬機運行時會忽略掉它不認識的屬性。關于虛擬機規范中預定義的屬性,這里不展開講了,列舉幾個常用的。
1.9.1 屬性的通用格式
attribute_info {
u2 attribute_name_index; //屬性名索引
u4 attribute_length; //屬性長度
u1 info[attribute_length]; //屬性的具體內容
}
1.9.2 ConstantValue 屬性
?ConstantValue 屬性表示一個常量字段的值。位于 field_info結構的屬性表中。
ConstantValue_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 constantvalue_index;//字段值在常量池中的索引,常量池在該索引處的項給出該屬性表示的常量值。(例如,值是long型的,在常量池中便是CONSTANT_Long)
}
1.9.3 Deprecated 屬性
?Deprecated 屬性是在 JDK 1.1 為了支持注釋中的關鍵詞@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; //操作數棧的最大深度值,jvm運行時根據該值分配棧幀
u2 max_locals; //局部變量表最大存儲空間,單位是slot
u4 code_length; // 字節碼指令的個數
u1 code[code_length]; // 具體的字節碼指令
u2 exception_table_length; //異常的個數
{ u2 start_pc;
u2 end_pc;
u2 handler_pc; //當字節碼在[start_pc, end_pc)區間出現catch_type或子類,則轉到handler_pc行繼續處理。
u2 catch_type; //當catch_type=0,則任意異常都需轉到handler_pc處理
} exception_table[exception_table_length]; //具體的異常內容
u2 attributes_count; //屬性的個數
attribute_info attributes[attributes_count]; //具體的屬性內容
}
- 其中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 結構的屬性表中就必須含有對應的 InnerClasses 屬性。InnerClasses 屬性是在 JDK 1.1 中為了支持內部類和內部接口而引入的,位于 ClassFile結構的屬性表。
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結構的屬性表。它被調試器用于確定源文件中行號表示的內容在 Java 虛擬機的 code[]數組中對應的部分。在 Code 屬性的屬性表中,LineNumberTable 屬性可以按照任意順序出現,此外,多個 LineNumberTable屬性可以共同表示一個行號在源文件中表示的內容,即 LineNumberTable 屬性不需要與源文件的行一一對應。
LineNumberTable_attribute {
u2 attribute_name_index;//屬性名稱在常量池的索引,指向一個 CONSTANT_Utf8_info結構。
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屬性的屬性表中。它被調試器用于確定方法在執行過程中局部變量的信息。在 Code 屬性的屬性表中,LocalVariableTable 屬性可以按照任意順序出現。 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結構的屬性表中。在 Java 語言中,任何類、 接口、 初始化方法或成員的泛型簽名如果包含了類型變量( Type Variables) 或參數化類型( Parameterized Types),則 Signature 屬性會為它記錄泛型簽名信息。
Signature_attribute {
u2 attribute_name_index;//屬性名稱在常量池中的索引,指向一個 CONSTANT_Utf8_info結構。
u4 attribute_length;
u2 signature_index;
}
- slot是虛擬機未局部變量分配內存使用的最小單位。對于byte/char/float/int/short/boolean/returnAddress等長度不超過32位的局部變量,每個占用1個Slot;對于long和double這兩種64位的數據類型則需要2個Slot來存放。
- 實例方法中有隱藏參數this, 顯式異常處理器的參數,方法體定義的局部變量都使用局部變量表來存放。
- max_locals,不是所有局部變量所占Slot之和,因為Slot可以重用,javac編譯器會根據變量的作用域來分配Slot給各個變量使用,從而計算出max_locals大小。
- 虛擬機規范限制嚴格方法不允許超過65535個字節碼,否則拒絕編譯。
參考文獻:
《Java虛擬機規范 Java SE 8版》
周志明:《深入理解Java虛擬機:JVM高級特性與最佳實踐(最新第二版)》