Java class檔案結構(規範篇)
每一個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個位元組碼,否則拒絕編譯。
作者:SunnyMore
連結:https://www.jianshu.com/p/68520593b999