Class文件结构amp;字节码指令
- class文件結構
- 無符號數
- 表
- Class文件設計理念和意義
- 1. 魔數magic
- 證明magic作用
- minor_version、major_version
- 2. constant_pool_count
- 3. constant_pool[]常量池
- 3.1 CONSTANT_Class_info結構
- 3.2 CONSTANT_Fieldref_info, CONSTANT_Methodref_info和CONSTANT_InterfaceMethodref_info結構
- 3.3 CONSTANT_String_info結構
- 3.4CONSTANT_Integer_info和CONSTANT_Float_info結構
- 3.5CONSTANT_Long_info和CONSTANT_Double_info結構
- 3.6 CONSTANT_NameAndType_info結構
- 3.7 CONSTANT_Utf8_info結構
- 3.8 CONSTANT_MethodHandle_info結構
- 3.9 CONSTANT_MethodType_info結構
- 3.10 CONSTANT_InvokeDynamic_info結構
- 4. access_flags:訪問標志
- 5. this_class:類索引
- 6. super_class:父類索引
- 7. interfaces_count:接口計數器
- 8. interfaces[]:接口表
- 9. fields_count:字段計數器
- 10. fields[]:字段表
- 11. methods_count:方法計數器
- 12. methods[]:方法表
- 13. attributes_count:屬性計數器
- 14. attributes[]:屬性表
- 14.1 ConstantValue屬性
- 14.2 Code屬性
- 14.3 StackMapTable屬性
- 字節碼指令
- 常量入棧指令
- 局部變量值轉載到棧中指令
- 將棧頂值保存到局部變量中指令
- wide指令
- 通用(無類型)棧操作指令
- 類型轉換指令
- 整數運算
- 浮點運算
- 邏輯運算——移位運算
- 邏輯運算——按位布爾運算
- 控制流指令——條件跳轉指令
- 控制流指令——比較指令
- 控制流指令——無條件跳轉指令
- 控制流指令——表跳轉指令
- 控制流指令——異常和finally
- 對象操作指令
- 數組操作指令
- 方法調用指令
- 方法返回指令
- 線程同步指令
- 一個簡單的demo分析
- Test.java
- javap -v Test.class
- class文件結構
class文件結構
Class文件存儲的內容稱為字節碼(ByteCode),包含了JVM指令集和符號表以及若干其他輔助信息。
class文件是一組以8位字節為基礎單位的二進制流,各個數據項目嚴格按照順序緊湊的排列在Class文件中,中間沒有添加任何分隔符,整個Class文件中存儲的內容幾乎全部是程序運行的必要的數據,沒有空隙存在。
當遇到8位字節以上的空間的數據項時,則會按照高位在前的方式分割成若干個8位字節進行存儲。
Class文件中有兩種數據類型,分別是無符號數和表。
無符號數
無符號數屬于基本的數據類型,以u1、u2、u4、u8來表示一個字節、兩個字節…的無符號數;無符號數用來描述數字、索引引用、數量值或UTF-8編碼構成的字符串值。
表
表是由多個無符號數或其他表作為數據項構成的復合數據類型,一般以”_info”結尾,用來描述class文件的數據結構。
特點:節省存儲空間,提高處理性能
ClassFile { 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]; }- 魔數
- Class文件版本
- 常量池
- 訪問標志
- 類索引,父類索引,接口索引集合
- 字段表集合
- 方法表集合
- 屬性表集合
u2表示無符號數2個字節
u4表示無符號數4個字節
Class文件設計理念和意義
1. 魔數magic
魔數的唯一作用是確定這個文件是否為一個能被虛擬機所接受的Class文件。魔數值固定為0xCAFEBABE,不會改變。
證明magic作用
創建一個class文件 magic.class ,內容是magic test,直接運行java magic操作:
84407@FantJ MINGW64 ~/Desktop $ java magictest ????: ???????? magictest ????? LinkageErrorjava.lang.ClassFormatError: Incompatible magic value 1886741100 in class file magictest報錯意思是:magic矛盾,然后給了個magic value的十進制數,那么可以識別的magic十進制應該是多少呢。
應該是3405691582
那么,然后我用javac編譯的正常java文件生成class文件,用binary viewer 查看:
minor_version、major_version
魔數往后后面四位:表示字節碼版本,分別表示Class文件的副、主版本。當今用的最廣的幾個版本:
jdk1.8:52
jdk1.7:51
jdk1.6:50
對應版本號是52,是jdk1.8
版本向下兼容
2. constant_pool_count
常量池計數器,值等于constant_pool表中的成員數加1,占用兩個字節
3. constant_pool[]常量池
Java虛擬機指令執行時依賴常量池(constant_pool)表中的符號信息。
所有的常量池項都具有如下通用格式:
cp_info {u1 tag; u1 info[]; }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 |
3.1 CONSTANT_Class_info結構
表示類或接口
CONSTANT_Class_info {u1 tag; u2 name_index; }name_index必須是對常量池的一個有效索引
3.2 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; }class_index必須是對常量池的有效索引,常量池在該索引處的項必須是CONSTANT_Class_info結構,表示一個類或接口,當前字段或方法是這個類或接口的成員。
CONSTANT_Methodref_info結構的class_index項的類型必須是類(不能是接口)。CONSTANT_InterfaceMethodref_info結構的class_index項的類型必須是接口(不能是類)。CONSTANT_Fieldref_info結構的class_index項的類型既可以是類也可以是接口。
name_and_type_index必須是對常量池的有效索引,表示當前字段或方法的名字和描述符。
在一個CONSTANT_Fieldref_info結構中,給定的描述符必須是字段描述符。而CONSTANT_Methodref_info和CONSTANT_InterfaceMethodref_info中給定的描述符必須是方法描述符。
3.3 CONSTANT_String_info結構
用來表示String的結構
CONSTANT_String_info {u1 tag;u2 string_index; }string_index必須是對常量池的有效索引,常量池在該索引處的項必須是CONSTANT_Utf8_info
結構,表示一組Unicode碼點序列,這組Unicode碼點序列最終會被初始化為一個String對象。
3.4CONSTANT_Integer_info和CONSTANT_Float_info結構
表示4字節(int和float)的數值常量:
CONSTANT_Integer_info {u1 tag; u4 bytes; } CONSTANT_Float_info { u1 tag; u4 bytes; }3.5CONSTANT_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; }3.6 CONSTANT_NameAndType_info結構
表示字段或方法,但是和前面介紹的3個結構不同,CONSTANT_NameAndType_info結構沒有標識出它所屬的類或接口
CONSTANT_NameAndType_info { u1 tag; u2 name_index; u2 descriptor_index; }name_index項的值必須是對常量池的有效索引,常量池在該索引處的項必須是CONSTANT_Utf8_info結構,這個結構要么表示特殊的方法名,要么表示一個有效的字段或方法的非限定名(Unqualified Name)。
descriptor_index項的值必須是對常量池的有效索引,常量池在該索引處的項必須是CONSTANT_Utf8_info結構,這個結構表示一個有效的字段描述符或方法描述符。
3.7 CONSTANT_Utf8_info結構
用于表示字符串常量的值
CONSTANT_Utf8_info {u1 tag; u2 length; u1 bytes[length]; }CONSTANT_Utf8_info結構中的內容是以length屬性確定長度的
3.8 CONSTANT_MethodHandle_info結構
表示方法句柄
CONSTANT_MethodHandle_info {u1 tag;u1 reference_kind;u2 reference_index; }reference_kind項的值必須在1至9之間(包括1和9),它決定了方法句柄的類型。
1. 如果reference_kind項的值為1(REF_getField)、2(REF_getStatic)、3(REF_putField)或4(REF_putStatic),那么常量池在reference_index索引處的項必須是CONSTANT_Fieldref_info結構,表示由一個字段創建的方法句柄。
2. 如果reference_kind項的值是5(REF_invokeVirtual)、6(REF_invokeStatic)、7(REF_invokeSpecial)或8(REF_newInvokeSpecial),那么常量池在reference_index索引處的項必須是CONSTANT_Methodref_info結構,表示由類的方法或構造函數創建的方法句柄。
3. 如果reference_kind項的值是9(REF_invokeInterface),那么常量池在reference_index索引處的項必須是CONSTANT_InterfaceMethodref_info結構,表示由接口方法創建的方法句柄。
4. 如果reference_kind項的值是5(REF_invokeVirtual)、6(REF_invokeStatic)、7(REF_invokeSpecial)或9(REF_invokeInterface),那么方法句柄對應的方法不能為實例初始化()方法或類初始化方法()。
5. 如果reference_kind項的值是8(REF_newInvokeSpecial),那么方法句柄對應的方法必須為實例初始化()方法。
3.9 CONSTANT_MethodType_info結構
表示方法類型
CONSTANT_MethodType_info { u1 tag; u2 descriptor_index; }3.10 CONSTANT_InvokeDynamic_info結構
表示invokedynamic指令所使用到的引導方法(Bootstrap Method)、引導方法使用到動態調用名稱(Dynamic Invocation Name)、參數和請求返回類型、以及可以選擇性的附加被稱為靜態參數(Static Arguments)的常量序列。
CONSTANT_InvokeDynamic_info { u1 tag; u2 bootstrap_method_attr_index; u2 name_and_type_index; }bootstrap_method_attr_index項的值必須是對當前Class文件中引導方法表的bootstrap_methods[]數組的有效索引。
name_and_type_index項的值必須是對當前常量池的有效索引,常量池在該索引處的項必須是CONSTANT_NameAndType_info結構,表示方法名和方法描述符。
4. access_flags:訪問標志
訪問標志,access_flags是一種掩碼標志,用于表示某個類或者接口的訪問權限及基礎屬性。access_flags的取值范圍和相應含義見下表。
| 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 | 標識枚舉類型 |
5. this_class:類索引
this_class的值必須是對constant_pool表中項目的一個有效索引值。
是一個對constant_pool表中項目的一個有效索引值,表示指向常量池的第幾個位置。
6. super_class:父類索引
表示這個Class文件所定義的類的直接父類,如果Class文件的super_class的值為0,那這個Class文件只可能是定義的是java.lang.Object類,只有它是唯一沒有父類的類
是一個對constant_pool表中項目的一個有效索引值,表示指向常量池的第幾個位置。
7. interfaces_count:接口計數器
表示有這個類有幾個接口。
8. interfaces[]:接口表
成員所表示的接口順序和對應的源代碼中給定的接口順序(從左至右)一樣,即interfaces[0]對應的是源代碼中最左邊的接口。
是一個對constant_pool表中項目的一個有效索引值,表示指向常量池的第幾個位置。
表示當前類或接口的直接父接口數量
9. fields_count:字段計數器
表示當前Class文件fields[]數組的成員個數
10. fields[]:字段表
每個成員都必須是一個fields_info結構的數據項,描述當前類或接口聲明的所有字段,但不包括從父類或父接口繼承的部分。
用于表示當前類或接口中某個字段的完整描述
field_info {u2 access_flags; u2 name_index; //對常量池的一個有效索引u2 descriptor_index; //對常量池的一個有效索引u2 attributes_count; //當前字段的附加屬性的數量attribute_info attributes[attributes_count]; }access_flags項的值是用于定義字段被訪問權限和基礎屬性的掩碼標志。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,表示字段為枚舉類型。 |
attributes表的每一個成員的值必須是attribute結構,一個字段可以有任意個關聯屬性。
11. methods_count:方法計數器
methods_count的值表示當前Class文件methods[]數組的成員個數,Methods[]數組中每一項都是一個method_info結構的數據項。
12. methods[]:方法表
method_info結構可以表示類和接口中定義的所有方法,包括實例方法、類方法、實例初始化方法方法和類或接口初始化方法方法。methods[]數組只描述當前類或接口中聲明的方法,不包括從父類或父接口繼承的方法。
methods[]數組中的每個成員都必須是一個method_info結構的數據項,用于表示當前類或接口中某個方法的完整描述。
method_info { u2 access_flags; u2 name_index; u2 descriptor_index; u2 attributes_count; attribute_info attributes[attributes_count]; }access_flags項的值是用于定義當前方法的訪問權限和基本屬性的掩碼標志,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 | 方法在源文件中不出現,由編譯器產生 |
name_index和 descriptor_index 兩屬性是對常量池的一個有效索引
attributes_count的項的值表示這個方法的附加屬性的數量。
attributes 表的每一個成員的值必須是attribute結構,一個方法可以有任意個與之相關的屬性。
13. attributes_count:屬性計數器
attributes表中每一項都是一個attribute_info結構的數據項。
attributes_count的值表示當前Class文件attributes表的成員個數。
14. attributes[]:屬性表
attributes表的每個項的值必須是attribute_info結構,在Class文件格式中的ClassFile結構、field_info結構,method_info結構和Code_attribute結構都有使用,所有屬性的通用格式如下:
attribute_info {u2 attribute_name_index; u4 attribute_length; u1 info[attribute_length]; }attribute_name_index必須是對當前Class文件的常量池的有效16位無符號索引。表示當前屬性的名字。
attribute_length項的值給出了跟隨其后的字節的長度,這個長度不包括attribute_name_index和attribute_name_index項的6個字節。
14.1 ConstantValue屬性
ConstantValue屬性是定長屬性,位于field_info結構的屬性表中。如果該字段為靜態類型(即field_info結構的access_flags項設置了ACC_STATIC標志),則說明這個field_info結構表示的常量字段值將被分配為它的ConstantValue屬性表示的值,這個過程也是類或接口申明的常量字段(Constant Field)初始化的一部分。這個過程發生在引用類或接口的類初始化方法執行之前。
ConstantValue_attribute { u2 attribute_name_index; u4 attribute_length; u2 constantvalue_index; }attribute_name_index項的值,必須是一個對常量池的有效索引。
attribute_length項的值固定為2。
constantvalue_index項的值,必須是一個對常量池的有效索引。
14.2 Code屬性
Code屬性是一個變長屬性,位于method_info結構的屬性表。一個Code屬性只為唯一一個方法、實例類初始化方法或類初始化方法保存Java虛擬機指令及相關輔助信息。所有Java虛擬機實現都必須能夠識別Code屬性。如果方法被聲明為native或者abstract類型,那么對應的method_info結構不能有明確的Code屬性,其它情況下,method_info有必須有明確的Code屬性。
Code_attribute {u2 attribute_name_index;u4 attribute_length; u2 max_stack;u2 max_locals;u4 code_length; u1 code[code_length]; u2 exception_table_length; { u2 start_pc;u2 end_pc; u2 handler_pc; u2 catch_type; } exception_table[exception_table_length]; u2 attributes_count; attribute_info attributes[attributes_count]; }attribute_name_index項的值必須是對常量池的有效索引
attribute_length項的值表示當前屬性的長度,不包括開始的6個字節。
max_stack項的值給出了當前方法的操作數棧在運行執行的任何時間點的最大深度。
max_locals項的值給出了分配在當前方法引用的局部變量表中的局部變量個數,包括調用此方法時用于傳遞參數的局部變量。long和double型的局部變量的最大索引是max_locals-2,其它類型的局部變量的最大索引是max_locals-1.
code_length項給出了當前方法的code[]數組的字節數,code_length的值必須大于0,即code[]數組不能為空。
code[]數組給出了實現當前方法的Java虛擬機字節碼。
exception_table_length項的值給出了exception_table[]數組的成員個數量。
exception_table[]數組的每個成員表示code[]數組中的一個異常處理器(Exception Handler)。exception_table[]數組中,異常處理器順序是有意義的(不能隨意更改)。
start_pc和end_pc兩項的值表明了異常處理器在code[]數組中的有效范圍。
handler_pc項表示一個異常處理器的起點
如果catch_type項的值不為0,那么它必須是對常量池的一個有效索引
attributes_count項的值給出了Code屬性中attributes表的成員個數。
屬性表的每個成員的值必須是attribute結構。一個Code屬性可以有任意數量的可選屬性與之關聯。
14.3 StackMapTable屬性
StackMapTable屬性是一個變長屬性,位于Code屬性的屬性表中。這個屬性會在虛擬機類加載的類型階段被使用。
StackMapTable_attribute { u2 attribute_name_index;u4 attribute_length; u2 number_of_entries; stack_map_frame entries[number_of_entries]; }attribute_name_index項的值必須是對常量池的有效索引
attribute_length項的值表示當前屬性的長度,不包括開始的6個字節。
number_of_entries項的值給出了entries表中的成員數量。Entries表的每個成員是都是一個stack_map_frame結構的項。
entries表給出了當前方法所需的stack_map_frame結構。
…更多的屬性就不在這一一貼了,太多了,需要的時候查官方文檔即可:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4
字節碼指令
java虛擬機指令由一個字節長度的,代表某種特定操作含義的數字(稱之為操作碼),以及隨后的代表此操作所需參數的操作數而構成。
操作碼的長度為1個字節,所以最大只有256條
常量入棧指令
局部變量值轉載到棧中指令
將棧頂值保存到局部變量中指令
wide指令
通用(無類型)棧操作指令
類型轉換指令
整數運算
浮點運算
邏輯運算——移位運算
邏輯運算——按位布爾運算
控制流指令——條件跳轉指令
控制流指令——比較指令
控制流指令——無條件跳轉指令
控制流指令——表跳轉指令
控制流指令——異常和finally
對象操作指令
數組操作指令
方法調用指令
方法返回指令
線程同步指令
指令參考:https://blog.csdn.net/web_code/article/details/12164733
一個簡單的demo分析
Test.java
public class Test {public static void main(String[] args) {int a = 10;int b = 20;int c = a+b;System.out.println(c);} }javap -v Test.class
#2 = Fieldref #24.#25 // java/lang/System.out:Ljava/io/PrintStream;#3 = Methodref #26.#27 // java/io/PrintStream.println:(I)Vpublic static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: (0x0009) ACC_PUBLIC, ACC_STATICCode:stack=2, locals=4, args_size=10: bipush 10 //把10擴展成int入棧2: istore_1 //將棧頂int類型值保存到局部變量1中3: bipush 20 //把20擴展成int入棧5: istore_2 //將棧頂int類型值保存到局部變量2中6: iload_1 //從局部變量1中裝載int類型值入棧 7: iload_2 //從局部變量2中裝載int類型值入棧 8: iadd // 將棧頂兩int類型數相加,結果入棧。9: istore_3 //將棧頂int類型值保存到局部變量3中10: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;獲取靜態字段的值。#2表示常量池的索引13: iload_314: invokevirtual #3 // Method java/io/PrintStream.println:(I)V 運行時方法綁定調用方法。17: return //void函數返回。總結
以上是生活随笔為你收集整理的Class文件结构amp;字节码指令的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 关于WPF中Popup中的一些用法的总结
- 下一篇: Fix一个随机出现的键盘弹出的issue