在 Android 編程中,若是要閱讀及修改字節碼,則須要針對 Class文件 結構進行解析翻譯。 JVM 定義 Class文件 結構及指令集,經過查閱字節碼錶及指令集可瞭解 class文件 的內容邏輯。下面經過整理涉及的全部表格內容及指令,供查詢使用。html
分有兩大部份內容:Class 文件結構描述表 和 字節碼指令表java
表格參考來自於 「深刻理解Java虛擬機:JVM高級特定及最佳實踐」
Class文件 是 Java文件 編譯而來,以 JVM 定義的特定結構來描述文件定義的內容。主要表格分類爲:面試
類型 | 名稱 | 描述 | 數量 |
---|---|---|---|
u4(4個字節) | magic | 肯定該文件是否爲一個能被虛擬機接受的Class文件,相似於ID | 1 |
u2(2個字節) | minot_version | 次版本號 | 1 |
u2(2個字節) | mahor_version | 主版本號 | 1 |
u2(2個字節) | constant_pool_count | 常量池容量計數值,從1開始計算,0則表示不引用任何一個常量池項目 | 1 |
cp_info | constant_pool | 常量池 | constant_pool_count-1 |
u2(2個字節) | access_flags | 訪問標誌 | 1 |
u2(2個字節) | this_class | 類索引 | 1 |
u2(2個字節) | super_class | 父類索引 | 1 |
u2(2個字節) | interfaces_count | 實現接口的數目 | 1 |
u2(4個字節) | interfaces | 接口索引 | interfaces_count |
u2(4個字節) | fields_count | 字段的數目 | 1 |
field_info | fields | 字段內容 | fields_count |
u2(2個字節) | methods_count | 方法的數目 | 1 |
method_info | methods | 方法內容 | methods_count |
u2(2個字節) | attributes_count | 屬性的數目 | 1 |
attribute_info | attributes | 屬性內容 | attributes_count |
常量池主要存放兩種類型:編程
Class文件 只保存各個方法,字段端的信息,不保存內存信息。只有通過運行期轉換才能獲得真正的內存入口。當虛擬機運行時,須要從常量池中獲取到對應的符號引用,再通過類建立或者運行時解析,獲得具體的內存地址。bootstrap
類型 | 子結構 | 標誌 | 描述 |
---|---|---|---|
CONSTANT_Utf8_info | tag | u1 = 1 | UTF-8編碼的字符串 |
- | lenght | u2 | UTF-8編碼的字符串佔用的字節數 |
- | bytes | u1 | 長度爲lenght的UTF-8編碼的字符串 |
CONSTANT_Integer_info | tag | u1=3 | 整型字面量 |
- | bytes | u4 | 按照高位在前存儲的int值 |
CONSTANT_Float_info | tag | u1=4 | 浮點型字面量 |
- | bytes | u4 | 按照高位在前存儲的float值 |
CONSTANT_Long_info | tag | u1=5 | 長整型字面量 |
- | bytes | u8 | 按照高位在前存儲的long值 |
CONSTANT_Double_info | tag | u1=6 | 雙精度浮點型字面量 |
- | bytes | u8 | 按照高位在前存儲的double值 |
CONSTANT_Class_info | tag | u1=7 | 類或接口的符號引用 |
- | bytes | u2 | 指向全限定名常量項的索引 |
CONSTANT_String_info | tag | u1=8 | 字符串類型字面量 |
- | bytes | u2 | 指向字符串字面量的索引 |
CONSTANT_Fieldref_info | tag | u1=9 | 字段的符號引用 |
- | index | u2 | 指向聲明字段的類或者接口描述符 CONSTANT_Class_info 的索引項 |
- | index | u2 | 指向聲明字段的類或者接口描述符CONSTANT_NameAndType_info 的索引項 |
CONSTANT_Methodred_info | tag | u1=10 | 類中方法的符號引用 |
- | index | u2 | 指向聲明字段的類或者接口描述符 CONSTANT_Class_info 的索引項 |
- | index | u2 | 指向聲明字段的類或者接口描述符CONSTANT_NameAndType_info 的索引項 |
CONSTANT_InterfaceMethodref_info | tag | u1=11 | 接口中方法的符號引用 |
- | index | u2 | 指向聲明字段的類或者接口描述符 CONSTANT_Class_info 的索引項 |
- | index | u2 | 指向聲明字段的類或者接口描述符CONSTANT_NameAndType_info 的索引項 |
CONSTANT_NameAndType_info | tag | u1=12 | 字段或方法的部分符號引用 |
- | index | u2 | 指向該字段或方法名稱常量項的索引 |
- | index | u2 | 指向該字段或方法名稱常量項的索引 |
CONSTANT_MethodHandle_info | tag | u1=15 | 表示方法句柄 |
- | reference_kind | u1 | 值必須在[1,9]中,它決定了方法句柄的類型。方法句柄類型的值表示方法句柄的字節碼行爲 |
- | reference_index | u2 | 值必須是對常量池的有效索引 |
CONSTANT_MethodType_info | tag | u1=16 | 識別方法類型 |
- | descriptor_index | u2 | 值必須是對常量池的有效索引,常量池在該索引處的項必須是CONSTANT_Utf8_info結構,表示方法的描述符 |
CONSTANT_InvokeDynamic_info | tag | u1=18 | 表示一個動態方法調用點 |
- | bootstrap_method_attar_index | u2 | 值必須是對當前Class文件中引導方法表的 bootstrap_methods[]數組的有效索引 |
- | name_and_type_index | u2 | 值必須是對當前常量池的有效索引,常量池在該索引處的值必須是CONSTANT_NameAndType_info結構,表示方法名和方法描述符 |
訪問標誌表根據如下不一樣標誌類型進一步劃分:數組
用於識別一些類或者接口層次的訪問信息,包括這個 Class文件 是類仍是接口,是否被定義成 public 類型,是否被定義成 abstract類 類型,若是是類的話,是否被聲明爲 final 等。微信
標誌名稱 | 標誌值 | 描述 |
---|---|---|
ACC_PUBLIC | 0x0001 | 是否爲public類型 |
ACC_FINAL | 0x0010 | 是否被聲明爲final,只有類可設置 |
ACC_SUPER | 0x0020 | 是否容許使用invokespecial字節碼指令的新語意,invokespecial指令的語意在JDK1.0.2發生過變化,爲了區別這條指令使用哪一種語意,JDK1.0.2以後編譯出來的類的這個標識必須都爲真 |
ACC_INTERFACE | 0x0200 | 標識這個是一個接口 |
ACC_ABSTRACT | 0x0400 | 是否爲abstract類型,對於接口或者抽象類來講,此標誌的值都爲真,其餘類型爲假 |
ACC_SYNTHETIC | 0x1000 | 標識這個類並不是由用戶代碼產生的 |
ACC_ANNOTATION | 0x2000 | 標識這是一個註解 |
ACC_ENUM | 0x4000 | 標識這是一個枚舉 |
標誌名稱 | 標誌值 | 描述 |
---|---|---|
ACC_PUBLIC | 0x0001 | 內部類是否爲public |
ACC_PRIVATE | 0x0002 | 內部類是否爲private |
ACC_PROTECTED | 0x0004 | 內部類是否爲protected |
ACC_STATIC | 0x0008 | 內部類是否爲protected |
ACC_FINAL | 0x0010 | 內部類是否爲protected |
ACC_INTERFACE | 0x0020 | 內部類是否爲接口 |
ACC_ABSTRACT | 0x0400 | 內部類是否爲abstract |
ACC_SYNTHETIC | 0x1000 | 內部類是否並不是由用戶代碼產生 |
ACC_ANNOTATION | 0x2000 | 內部類是不是一個註解 |
ACC_ENUM | 0x4000 | 內部類是不是一個枚舉 |
標誌名稱 | 標誌值 | 描述 |
---|---|---|
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 |
標誌名稱 | 標誌值 | 描述 |
---|---|---|
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 | 方法是否由編譯器產生的橋接方法 |
ACC_VARARGS | 0x0080 | 方法是否接受不定參數 |
ACC_NATIVE | 0x0100 | 方法是否爲native |
ACC_ABSTRACT | 0x0400 | 方法是否爲abstract |
ACC_STRICTFP | 0x0800 | 方法是否爲strictfp |
ACC_SYNTHETIC | 0x1000 | 方法是否由編譯器自動產生的 |
用於描述接口和類中聲明的變量,包括 類級別變量 及 實例級別變量。架構
類型 | 名稱 | 數量 |
---|---|---|
u2 | access_flags | 1 |
u2 | name_index | 1 |
u2 | descriptor_index | 1 |
u2 | attributes_count | 1 |
u2 | attributes | attributes_count |
其中 access_flags 見爲訪問標誌表中的字段訪問標誌。oracle
方法表包含訪問標誌,名稱索引,描述符索引以及屬性表等幾項jvm
類型 | 名稱 | 數量 |
---|---|---|
u2 | access_flags | 1 |
u2 | name_index | 1 |
u2 | descriptor_index | 1 |
u2 | attributes_count | 1 |
attribute_info | attributes | attributes_count |
其中方法的 access_flags 見上述的方法訪問標誌
屬性表是解釋 Class文件,字段表,方法表中攜帶的屬性的表格,屬性是用於描述某些場景專有的信息。
屬性名稱 | 使用位置 | 含義 |
---|---|---|
Code | 方法表 | Java代碼編譯成的字節碼指令 |
ConstantValue | 字段表 | final關鍵字定義的常量值 |
Deprecated | 類,方法表,字段表 | final關鍵字定義的常量值 |
Exceptions | 方法表 | final方法拋出的異常 |
EnclosingMethod | 類文件 | 僅當一個類爲局部類或者匿名類時才能擁有這個屬性,這個屬性用於標識這個類所在的外圍方法 |
InnerClasses | 類文件 | 內部類列表 |
LineNumberTable | Code屬性 | Java源碼的行號與字節碼指令的對應關係 |
LocalVariableTable | Code屬性 | 方法的局部變量描述 |
StackMapTable | Code屬性 | JDK1.6中新增的屬性,供新的類型檢查校驗器(Type Checker)檢查和處理目標方法的局部變量和操做數棧鎖須要的類型是否匹配 |
Signature | 類,方法表,字段表 | JDK1.5中新增的屬性,這個屬性用於支持泛型狀況下的方法簽名,在java語言中,任何類,接口,初始化方法或成員的泛型簽名若是包含了類型變量(Type Variables)或者參數化類型(Parameterized Types),則Signature屬性會爲它記錄泛型簽名信息。因爲java的泛型採用擦除法實現,在爲了類型信息被擦除後致使簽名混亂,須要這個屬性記錄泛型中的相關信息 |
SourceFile | 類文件 | 記錄源文件名稱 |
SourceDebugExtension | 類文件 | JDK1.6中新增的屬性,SourceDebugExtension屬性用於存儲額外的調試信息。譬如在進行JSP文件調試時,沒法經過Java堆棧來定位到JSP文件的行號,JSR-45規範爲這些非Java語言編寫,卻須要編譯成字節碼並運行在Java虛擬機中的程序提供了一個進行調試的標準機制,使用SourceDebugExtension屬性就能夠用於存儲這個標準所新加入的調試信息 |
Synthetic | 類,方法表,字段表 | 標識方法或者字段是否爲編譯器自動生成的 |
LocalVariableTypeTable | 類 | JDK1.5中新增的屬性,它使用特徵簽名代替描述符,是爲了引入泛型語法以後能描述泛型參數化類型而添加的 |
RuntimevisibleAnnotations | 類,方法表,字段表 | JDK1.5中新增的屬性,爲動態註解提供支持。RuntimevisibleAnnotations 屬性用於指明哪些註解是運行時(實際上運行時就是進行反射調用)可見的 |
RuntimeInvisibleAnnotations | 類,方法表,字段表 | JDK1.5中新增的屬性,與 RuntimevisibleAnnotations 屬性做用恰好相反, 用於指明哪些註解是運行時不可見的 |
RuntimeVisibleParameterAnnotations | 方法表 | JDK1.5中新增的屬性,做用與 RuntimevisibleAnnotations 屬性相似,只不過做用對象爲方法參數 |
RuntimeInvisibleParameterAnnotations | 方法表 | JDK1.5中新增的屬性,做用與 RuntimeInvisibleAnnotations 屬性相似,只不過做用對象爲方法參數 |
AnnotationDetault | 方法表 | JDK1.5中新增的屬性,用於記錄註解類元素的默認值 |
BootstrapMethods | 類文件 | JDK1.5中新增的屬性,用於保存 invokedynamic 指令引用的引導方法限定符 |
上述的每個屬性都須要從常量池中引用一個 CONSTANT_Utf8_info 類型常量來標示。還包含 attribute_length(u4) 用於標示屬性值所佔用的位數,後面再跟着屬性內容。
類型 | 名稱 | 數量 |
---|---|---|
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
u2 | max_stack | 1 |
u2 | max_locals | 1 |
u4 | code_length | 1 |
u1 | code | code_lenght |
u2 | exception_table_lenght | 1 |
exception_info | exception_table | exception_table_length |
u2 | attributes_count | 1 |
attribute_info | attributes | attributes_count |
類型 | 名稱 | 數量 |
---|---|---|
u2 | start_pc | 1 |
u2 | end_pc | 1 |
u2 | handler_pc | 1 |
u2 | catch_type | 1 |
區別與異常表,該表主要是列舉中方法中可能拋出的受檢查異常,也就是方法描述時throws關鍵字列舉的異常
類型 | 名稱 | 數量 |
---|---|---|
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
u2 | number_of_exceptions | 1 |
u2 | exception_index_table | number_of_exceptions |
用於描述Java源碼行號與字節碼行號之間的對應關係,默認生成到 Class文件 中。
類型 | 名稱 | 數量 |
---|---|---|
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
u2 | line_number_table_length | 1 |
line_number_info | line_number_table | line_number_table_length |
其中line_number_info包含start_pc和line_number兩個u2類型的數據項。
用於描述棧幀中局部變量表中的變量與 Java 源碼中定義的變量之間的關係,默認生成到 Class文件 中。
類型 | 名稱 | 數量 |
---|---|---|
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
u2 | local_variable_table_lenght | 1 |
local_variable_info | local_variable_table | local_variable_table_lenght |
其中 local_variable_info 是表明棧幀與源碼中局部變量的關聯,見下表:
類型 | 名稱 | 含義 | 數量 |
---|---|---|---|
u2 | start_pc | 局部變量的生命週期開始的字節碼偏移量 | 1 |
u2 | length | 局部變量的生命週期開始的做用範圍覆蓋長度 | 1 |
u2 | name_index | 指向常量池 CONSTANT_Utf8_info 索引 | 1 |
u2 | descriptor_index | 指向常量池 CONSTANT_Utf8_info 索引 | 1 |
u2 | index | 局部變量在棧幀局部變量表中Slot的位置 | 1 |
用於記錄生成該 Class文件 的源碼文件名稱。
類型 | 名稱 | 數量 |
---|---|---|
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
u2 | sourcefile_index | 1 |
其中 sourcefile_index 爲指向常量池 CONSTANT_Utf8_info 索引。
用於通知虛擬機自動爲靜態變量賦值。只有被 static 關鍵字修飾的變量纔可使用這項屬性。
類型 | 名稱 | 數量 |
---|---|---|
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
u2 | constant_index | 1 |
用於記錄內部類與宿主類之間的關聯。若是一個類中定義了內部類,編譯器則會爲它生成內部類 InnerClasses 屬性。
類型 | 名稱 | 數量 |
---|---|---|
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
u2 | number_of_classes | 1 |
inner_classes_info | inner_classes | number_of_classes |
每個 inner_classes_info 表明一個內部類信息,結構以下:
類型 | 名稱 | 含義 | 數量 |
---|---|---|---|
u2 | inner_class_info_index | 指向常量池 CONSTANT_Class_info 索引 | 1 |
u2 | outer_class_info_index | 指向常量池 CONSTANT_Class_info 索引 | 1 |
u2 | inner_name_index | 指向常量池 CONSTANT_Utf8_info 索引,表明這個內部類的名稱,若是匿名則爲0 | 1 |
u2 | inner_class_access_flags | 內部類的訪問標誌,見上述訪問標誌篇章 | 1 |
前者是用於標示某個類,字段或者方法是否再也不推薦使用。
後者是用於標示字段或者方法不是由 Java 源碼直接產生。全部由非用戶代碼生成的方法都須要設置 Synthetic 屬性或者 ACC_SYNTHETIC 標誌,可是 <init>
和 <clinit>
除外。他們的結構以下:
類型 | 名稱 | 數量 |
---|---|---|
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
於 JDK1.6 以後添加在 Class 文件規範中,位於 Code屬性表 中,該屬性會在虛擬機類加載的字節碼校驗階段被新類型檢查檢驗器(Type Checker)使用。
類型 | 名稱 | 數量 |
---|---|---|
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
u2 | number_of_entries | 1 |
stack_map_frame | stack_map_frame_entries | number_of_entries |
於 JDK1.5 發佈以後添加到 Class 文件規範中,它是一個可選的定長屬性,可出如今類,屬性表,方法表結構的屬性表中。該屬性會記錄泛型簽名信息,在 Java 語言中泛型採用的是擦除法實現的僞泛型,在字節碼(Code屬性)中,泛型信息編譯以後都通通被擦除掉。因爲沒法像 C# 等運行時支持獲取真泛型類型,添加該屬性用於彌補該缺陷,如今 Java 反射已經能獲取到泛型類型。
類型 | 名稱 | 數量 |
---|---|---|
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
u2 | signature_index | 1 |
其中 signature_index 值必須是一個對常量池的有效索引且爲 CONSTANT_Utf8_info,表示類簽名,方法類型簽名或字段類型簽名。若是當前Signature屬性是類文件的屬性,則這個結構表示類簽名,若是當前Signature屬性是方法表的屬性,則表示方法類型簽名,若是當前Signature屬性是字段表的屬性,則表示字段類型簽名。
於 JDK1.7 發佈後添加到 Class 文件規範中,是一個複雜變長的屬性,位於類文件的屬性表中。
類型 | 名稱 | 數量 |
---|---|---|
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
u2 | num_bootstrap_methods | 1 |
bootstrap_method | bootstrap_methods | num_bootstrap_methods |
其中 bootstrap_method 結構以下
類型 | 名稱 | 數量 |
---|---|---|
u2 | bootstrap_method_ref | 1 |
u2 | num_bootstrap_arguments | 1 |
u2 | bootstrap_arguments | num_bootstrap_arguments |
所謂全限定名,就是使用 "." 分割類全名。好比 com/yummylau/TestClass 把類全名的 "." 換成 "/",變成 com.yummylau.TestClass,多個全限定名可以使用多個 ";"分割。
而簡單名稱則沒有類型和參數修飾的方法或者字段的名字,好比方法 inc() 和字段 m 分別標示爲 inc 和 m 。特殊字符串表包含一些基礎類型的描述及方法描述。以下:
標識字符 | 含義 |
---|---|
B | 基本類型 byte |
C | 基本類型 char |
D | 基本類型 double |
F | 基本類型 float |
I | 基本類型 int |
J | 基本類型 long |
S | 基本類型 short |
Z | 基本類型 boolean |
V | 基本類型 void |
L | 對象類型,好比 Ljava/lang/Object |
針對數組,每個維度使用一個前置的"["字符來描述,好比定義一個 「java.lang.String[][]」數組,被記錄爲「[[java.lang.String;」一個整型數組 「int[]」 被記錄爲[I
針對方法
方法場景 | 描述符 |
---|---|
void inc() | ()V |
java.lang.String toString() | ()Ljava/lang/String; |
int indexOf(char[]source,int sourceOffest,int sourceCount,char[] target,int targetOffset,int targetCOunt,int formIndex) | ([CII[CIII)I |
按照指令的類型/目標數據類型/經常使用指令等進一步劃分爲如下內容:
tip : 更爲具體的描述可參考 官方JVM指令文檔
字節碼 | 助記符 | 指令含義 |
---|---|---|
0x00 | nop | 什麼都不作 |
0x01 | aconst_null | 將 null 推送至棧頂 |
0x02 | iconst_m1 | 將 int 型 -1 推送至棧頂 |
0x03 | iconst_0 | 將 int 型 0 推送至棧頂 |
0x04 | iconst_1 | 將 int 型 1 推送至棧頂 |
0x05 | iconst_2 | 將 int 型 2 推送至棧頂 |
0x06 | iconst_3 | 將 int 型 3 推送至棧頂 |
0x07 | iconst_4 | 將 int 型 4 推送至棧頂 |
0x08 | iconst_5 | 將 int 型 5 推送至棧頂 |
0x09 | lconst_0 | 將 long 型 0 推送至棧頂 |
0x0a | lconst_1 | 將 long 型 1 推送至棧頂 |
0x0b | fconst_0 | 將 float 型 0 推送至棧頂 |
0x0c | fconst_1 | 將 float 型 1 推送至棧頂 |
0x0d | fconst_2 | 將 float 型 2 推送至棧頂 |
0x0e | dconst_0 | 將 double 型 0 推送至棧頂 |
0x0f | dconst_1 | 將 double 型 1 推送至棧頂 |
0x10 | bipush | 將單字節的常量(-128 - 127)推送至棧頂 |
0x11 | sipush | 將一個短整形常量常量(-32768 - 32767)推送至棧頂 |
0x12 | ldc | 將 int, float, String 型常量值從常量池中推送至棧頂 |
0x13 | ldc_w | 將 int, float, String 型常量值從常量池中推送至棧頂(寬索引) |
0x14 | ldc2_w | 將 long 或 float 型常量值從常量池中推送至棧頂(寬索引) |
0x15 | iload | 將指定的 int 型本地變量推送至棧頂 |
0x16 | lload | 將指定的 long 型本地變量推送至棧頂 |
0x17 | fload | 將指定的 float 型本地變量推送至棧頂 |
0x18 | dload | 將指定的 dload 型本地變量推送至棧頂 |
0x19 | aload | 將指定的引用類型本地變量推送至棧頂 |
0x1a | iload_0 | 將第一個 int 型本地變量推送至棧頂 |
0x1b | iload_1 | 將第二個 int 型本地變量推送至棧頂 |
0x1c | iload_2 | 將第三個 int 型本地變量推送至棧頂 |
0x1d | iload_3 | 將第四個 int 型本地變量推送至棧頂 |
0x1e | lload_0 | 將第一個 long 型本地變量推送至棧頂 |
0x1f | lload_1 | 將第二個 long 型本地變量推送至棧頂 |
0x20 | lload_2 | 將第三個 long 型本地變量推送至棧頂 |
0x21 | lload_3 | 將第四個 long 型本地變量推送至棧頂 |
0x22 | fload_0 | 將第一個 float 型本地變量推送至棧頂 |
0x23 | fload_1 | 將第二個 float 型本地變量推送至棧頂 |
0x24 | fload_2 | 將第三個 float 型本地變量推送至棧頂 |
0x25 | fload_3 | 將第四個 float 型本地變量推送至棧頂 |
0x26 | dload_0 | 將第一個 double 型本地變量推送至棧頂 |
0x27 | dload_1 | 將第二個 double 型本地變量推送至棧頂 |
0x28 | dload_2 | 將第三個 double 型本地變量推送至棧頂 |
0x29 | dload_3 | 將第四個 double 型本地變量推送至棧頂 |
0x2a | aload_0 | 將第一個引用類型本地變量推送至棧頂 |
0x2b | aload_1 | 將第二個引用類型本地變量推送至棧頂 |
0x2c | aload_2 | 將第三個引用類型本地變量推送至棧頂 |
0x2d | aload_3 | 將第四個引用類型本地變量推送至棧頂 |
0x2e | iaload | 將 int 型數組指定索引的值推送至棧頂 |
0x2f | laload | 將 long 型數組指定索引的值推送至棧頂 |
0x30 | faload | 將 float 型數組指定索引的值推送至棧頂 |
0x31 | daload | 將 double 型數組指定索引的值推送至棧頂 |
0x32 | aaload | 將引用型數組指定索引的值推送至棧頂 |
0x33 | baload | 將 boolean 或 byte 型數組指定索引的值推送至棧頂 |
0x34 | caload | 將 char 型數組指定索引的值推送至棧頂 |
0x35 | saload | 將 short 型數組指定索引的值推送至棧頂 |
0x36 | istore | 將棧頂 int 型數值存入指定本地變量 |
0x37 | lstore | 將棧頂 long 型數值存入指定本地變量 |
0x38 | fstore | 將棧頂 float 型數值存入指定本地變量 |
0x39 | dstore | 將棧頂 double 型數值存入指定本地變量 |
0x3a | astore | 將棧頂引用型數值存入指定本地變量 |
0x3b | istore_0 | 將棧頂 int 型數值存入第一個本地變量 |
0x3c | istore_1 | 將棧頂 int 型數值存入第二個本地變量 |
0x3d | istore_2 | 將棧頂 int 型數值存入第三個本地變量 |
0x3e | istore_3 | 將棧頂 int 型數值存入第四個本地變量 |
0x3f | lstore_0 | 將棧頂 long 型數值存入第一個本地變量 |
0x40 | lstore_1 | 將棧頂 long 型數值存入第二個本地變量 |
0x41 | lstore_2 | 將棧頂 long 型數值存入第三個本地變量 |
0x42 | lstore_3 | 將棧頂 long 型數值存入第四個本地變量 |
0x43 | fstore_0 | 將棧頂 float 型數值存入第一個本地變量 |
0x44 | fstore_1 | 將棧頂 float 型數值存入第二個本地變量 |
0x45 | fstore_2 | 將棧頂 float 型數值存入第三個本地變量 |
0x46 | fstore_3 | 將棧頂 float 型數值存入第四個本地變量 |
0x47 | dstore_0 | 將棧頂 double 型數值存入第一個本地變量 |
0x48 | dstore_1 | 將棧頂 double 型數值存入第二個本地變量 |
0x49 | dstore_2 | 將棧頂 double 型數值存入第三個本地變量 |
0x4a | dstore_3 | 將棧頂 double 型數值存入第四個本地變量 |
0x4b | astore_0 | 將棧頂引用型數值存入第一個本地變量 |
0x4c | astore_1 | 將棧頂引用型數值存入第二個本地變量 |
0x4d | astore_2 | 將棧頂引用型數值存入第三個本地變量 |
0x4e | astore_3 | 將棧頂引用型數值存入第四個本地變量 |
0x4f | iastore | 將棧頂 int 型數值存入指定數組的指定索引位置 |
0x50 | lastore | 將棧頂 long 型數值存入指定數組的指定索引位置 |
0x51 | fastore | 將棧頂 float 型數值存入指定數組的指定索引位置 |
0x52 | dastore | 將棧頂 double 型數值存入指定數組的指定索引位置 |
0x53 | aastore | 將棧頂引用型數值存入指定數組的指定索引位置 |
0x54 | bastore | 將棧頂 boolean 或 byte 型數值存入指定數組的指定索引位置 |
0x55 | castore | 將棧頂 char 型數值存入指定數組的指定索引位置 |
0x56 | sastore | 將棧頂 short 型數值存入指定數組的指定索引位置 |
0x57 | pop | 將棧頂數值彈出(數值不能是 long 或 double 類型) |
0x58 | pop_2 | 將棧頂的一個(對於 long 或 double 類型)或兩個數值(對於非 long 或 double 的其餘類型)彈出 |
0x59 | dup | 複製棧頂數值並將複製值壓入棧頂 |
0x5a | dup_x1 | 複製棧頂數值並將兩個複製值壓入棧頂 |
0x5b | dup_x2 | 複製棧頂數值並將三個(或兩個)複製值壓入棧頂 |
0x5c | dup_2 | 複製棧頂一個(對於 long 或 double 類型)或兩個(非 long 或 double 的其餘類型)數值並將複製值壓入棧頂 ) |
0x5d | dup_2_x1 | dup_x1 指令的雙倍版本 |
0x5e | dup_2_x2 | dup_x2 指令的雙倍版本 |
0x5f | swap | 將棧最頂端的兩個數值互換(數值不能是 long 或 double 類型) |
0x60 | iadd | 將棧頂兩 int 型數值相加並將結果壓入棧頂 |
0x61 | ladd | 將棧頂兩 long 型數值相加並將結果壓入棧頂 |
0x62 | fadd | 將棧頂兩 float 型數值相加並將結果壓入棧頂 |
0x63 | dadd | 將棧頂兩 double 型數值相加並將結果壓入棧頂 |
0x64 | isub | 將棧頂兩 int 型數值相減並將結果壓入棧頂 |
0x65 | lsub | 將棧頂兩 long 型數值相減並將結果壓入棧頂 |
0x66 | fsub | 將棧頂兩 float 型數值相減並將結果壓入棧頂 |
0x67 | dsub | 將棧頂兩 double 型數值相減並將結果壓入棧頂 |
0x68 | imul | 將棧頂兩 int 型數值相乘並將結果壓入棧頂 |
0x69 | lmul | 將棧頂兩 long 型數值相乘並將結果壓入棧頂 |
0x6a | fmul | 將棧頂兩 float 型數值相乘並將結果壓入棧頂 |
0x6b | dmul | 將棧頂兩 double 型數值相乘並將結果壓入棧頂 |
0x6c | idiv | 將棧頂兩 int 型數值相除並將結果壓入棧頂 |
0x6d | ldiv | 將棧頂兩 long 型數值相除並將結果壓入棧頂 |
0x6e | fdiv | 將棧頂兩 float 型數值相除並將結果壓入棧頂 |
0x6f | ddiv | 將棧頂兩 double 型數值相除並將結果壓入棧頂 |
0x70 | irem | 將棧頂兩 int 型數值做取模運算並將結果壓入棧頂 |
0x71 | lrem | 將棧頂兩 long 型數值做取模運算並將結果壓入棧頂 |
0x72 | frem | 將棧頂兩 float 型數值做取模運算並將結果壓入棧頂 |
0x73 | drem | 將棧頂兩 double 型數值做取模運算並將結果壓入棧頂 |
0x74 | ineg | 將棧頂兩 int 型數值做負並將結果壓入棧頂 |
0x75 | lneg | 將棧頂兩 long 型數值做負並將結果壓入棧頂 |
0x76 | fneg | 將棧頂兩 float 型數值做負並將結果壓入棧頂 |
0x77 | dneg | 將棧頂兩 double 型數值做負並將結果壓入棧頂 |
0x78 | ishl | 將棧頂兩 int 型數值左移位指定位數並將結果壓入棧頂 |
0x79 | lshl | 將棧頂兩 long 型數值左移位指定位數並將結果壓入棧頂 |
0x7a | ishr | 將棧頂兩 int 型數值右(帶符號)移位指定位數並將結果壓入棧頂 |
0x7b | lshr | 將棧頂兩 long 型數值右(帶符號)移位指定位數並將結果壓入棧頂 |
0x7c | iushr | 將棧頂兩 int 型數值右(無符號)移位指定位數並將結果壓入棧頂 |
0x7d | lushr | 將棧頂兩 long 型數值右(無符號)移位指定位數並將結果壓入棧頂 |
0x7e | iand | 將棧頂兩 int 型數值做 「按位與」 並將結果壓入棧頂 |
0x7f | land | 將棧頂兩 long 型數值做 「按位與」 並將結果壓入棧頂 |
0x80 | ior | 將棧頂兩 int 型數值做 「按位或」 並將結果壓入棧頂 |
0x81 | lor | 將棧頂兩 long 型數值做 「按位或」 並將結果壓入棧頂 |
0x82 | ixor | 將棧頂兩 int 型數值做 「按位異或」 並將結果壓入棧頂 |
0x83 | lxor | 將棧頂兩 long 型數值做 「按位異或」 並將結果壓入棧頂 |
0x84 | iinc | 將棧頂 int 型變量增長指定值(如i++, i--, i+=2等) |
0x85 | i2l | 將棧頂 int 型數值強制轉成 long 型數值並將結果壓入棧頂 |
0x86 | i2f | 將棧頂 int 型數值強制轉成 float 型數值並將結果壓入棧頂 |
0x87 | i2d | 將棧頂 int 型數值強制轉成 double 型數值並將結果壓入棧頂 |
0x88 | l2i | 將棧頂 long 型數值強制轉成 int 型數值並將結果壓入棧頂 |
0x89 | l2f | 將棧頂 long 型數值強制轉成 float 型數值並將結果壓入棧頂 |
0x8a | l2d | 將棧頂 long 型數值強制轉成 double 型數值並將結果壓入棧頂 |
0x8b | f2i | 將棧頂 float 型數值強制轉成 int 型數值並將結果壓入棧頂 |
0x8c | f2l | 將棧頂 float 型數值強制轉成 long 型數值並將結果壓入棧頂 |
0x8d | f2d | 將棧頂 float 型數值強制轉成 double 型數值並將結果壓入棧頂 |
0x8e | d2i | 將棧頂 double 型數值強制轉成 int 型數值並將結果壓入棧頂 |
0x8f | d2l | 將棧頂 double 型數值強制轉成 long 型數值並將結果壓入棧頂 |
0x90 | d2f | 將棧頂 double 型數值強制轉成 float 型數值並將結果壓入棧頂 |
0x91 | i2b | 將棧頂 int 型數值強制轉成 byte 型數值並將結果壓入棧頂 |
0x92 | i2c | 將棧頂 int 型數值強制轉成 char 型數值並將結果壓入棧頂 |
0x93 | i2s | 將棧頂 int 型數值強制轉成 short 型數值並將結果壓入棧頂 |
0x94 | lcmp | 比較棧頂兩 long 型數值的大小,並將結果(1, 0 或 -1)壓入棧頂 |
0x95 | fcmpl | 比較棧頂兩 float 型數值的大小,並將結果(1, 0 或 -1)壓入棧頂; 當其中一個數值爲 「NaN」 時,將 -1 壓入棧頂 |
0x96 | fcmpg | 比較棧頂兩 float 型數值的大小,並將結果(1, 0 或 -1)壓入棧頂; 當其中一個數值爲 「NaN」 時,將 1 壓入棧頂 |
0x97 | dcmpl | 比較棧頂兩 double 型數值的大小,並將結果(1, 0 或 -1)壓入棧頂; 當其中一個數值爲 「NaN」 時,將 -1 壓入棧頂 |
0x98 | dcmpg | 比較棧頂兩 double 型數值的大小,並將結果(1, 0 或 -1)壓入棧頂; 當其中一個數值爲 「NaN」 時,將 1 壓入棧頂 |
0x99 | ifeg | 當棧頂 int 型數值等於 0 時跳轉 |
0x9a | ifne | 當棧頂 int 型數值不等於 0 時跳轉 |
0x9b | iflt | 當棧頂 int 型數值小於 0 時跳轉 |
0x9c | ifge | 當棧頂 int 型數值大於或等於 0 時跳轉 |
0x9d | ifgt | 當棧頂 int 型數值大於 0 時跳轉 |
0x9e | ifle | 當棧頂 int 型數值小於或等於 0 時跳轉 |
0x9f | if_icmpeq | 比較棧頂兩 int 型數值的大小,當結果等於 0 時跳轉 |
0xa0 | if_icmpne | 比較棧頂兩 int 型數值的大小,當結果不等於 0 時跳轉 |
0xa1 | if_icmplt | 比較棧頂兩 int 型數值的大小,當結果小於 0 時跳轉 |
0xa2 | if_icmpge | 比較棧頂兩 int 型數值的大小,當結果大於或等於 0 時跳轉 |
0xa3 | if_icmpgt | 比較棧頂兩 int 型數值的大小,當結果大於 0 時跳轉 |
0xa4 | if_icmple | 比較棧頂兩 int 型數值的大小,當結果小於或等於 0 時跳轉 |
0xa5 | if_icmpeq | 比較棧頂兩引用型數值,當結果相等時跳轉 |
0xa6 | if_icmpnc | 比較棧頂兩引用型數值,當結果不相等時跳轉 |
0xa7 | goto | 無條件跳轉 |
0xa8 | jsr | 跳轉至指定的 16 位 offset 位置,並將 jsr 的下一條指令地址壓入棧頂 |
0xa9 | ret | 返回至本地變量指定的 index 的指令位置(通常與 jsr 或 jsr_w 聯合使用) |
0xaa | tableswitch | 用於 switch 條件跳轉, case 值連續(可變長度指令) |
0xab | lookupswitch | 用於 switch 條件跳轉, case 值連不續(可變長度指令) |
0xac | ireturn | 從當前方法返回 int |
0xad | lreturn | 從當前方法返回 long |
0xae | freturn | 從當前方法返回 float |
0xaf | dreturn | 從當前方法返回 double |
0xb0 | areturn | 從當前方法返回對象引用 |
0xb1 | return | 從當前方法返回 void |
0xb2 | getstatic | 獲取指定類的靜態域,並將其值壓入棧頂 |
0xb3 | putstatic | 爲指定的類的靜態域賦值 |
0xb4 | getfield | 獲取指定類的實例域,並將其值壓入棧頂 |
0xb5 | putfield | 爲指定的類的實例域賦值 |
0xb6 | invokevirtual | 調用實例方法 |
0xb7 | invokespecial | 調用超類構造方法, 實例初始化方法,私有方法 |
0xb8 | invokestatic | 調用靜態方法 |
0xb9 | invokeinterface | 調用接口方法 |
0xba | invokedynamic | 調用動態方法 |
0xbb | new | 建立一個對象,並將其引用值壓入棧頂 |
0xbc | newarray | 建立一個指定的原始類型(如 int, float等)的數組,並將其引用值壓入棧頂 |
0xbd | anewarray | 建立一個引用型(如 類,接口,數組)的數組,並將其引用值壓入棧頂 |
0xbe | arraylength | 得到數組的長度值並壓入棧頂 |
0xbf | athrow | 將棧頂的異常拋出 |
0xc0 | checkcast | 檢驗類型轉換, 檢驗未經過將拋出 ClassCastException |
0xc1 | instanceof | 檢驗對象是否時指定類的實例, 若是是, 則將 1 壓入棧頂,不然將 0 壓入棧頂 |
0xc2 | monitorenter | 得到對象的鎖,用於同步方法或同步塊 |
0xc3 | monitorexit | 釋放對象的鎖,用於同步方法或同步塊 |
0xc4 | wide | 擴展本地變量的寬度 |
0xc5 | multianewarray | 建立指定類型和指定維度的多維數組(執行該指令時,操做棧中必須包含各維度的長度值),並將其引用值壓入棧頂 |
0xc6 | ifnull | 爲 null 時跳轉 |
0xc7 | ifnonnull | 不爲 null 時跳轉 |
0xc8 | goto_w | 無條件跳轉(寬索引) |
0xc9 | jsr_w | 跳轉至指定的 32 位 offset 位置,並將 jsr_w 的下一條指令地址壓入棧頂 |
數據類型 | byte | short | int | long | float | double | char | reference |
---|---|---|---|---|---|---|---|---|
簡化轉化 | b | s | i | l | f | d | c | a |
下面表格中T+指令構成 opcode
, T 爲上面表格各數據類型的簡化轉化。
opcode | byte | short | int | long | float | double | char | reference | |
---|---|---|---|---|---|---|---|---|---|
Tipush | bipush | sipush | |||||||
Tconst | iconst | lconst | fconst | dconst | aconst | ||||
Tload | iload | lload | fload | dload | aload | ||||
Tstore | istore | lstore | fstore | dstore | astore | ||||
Tinc | iinc | ||||||||
Taload | baload | saload | iaload | laload | faload | daload | caload | aaload | |
Tastore | bastore | sastore | iastore | lastore | fastore | dastore | castore | aastore | |
Tadd | iadd | ladd | fadd | dadd | |||||
Tsub | isub | lsub | fsub | dsub | |||||
Tmul | imul | lmul | fmul | dmul | |||||
Tdiv | idiv | ldiv | fdiv | ddiv | |||||
Trem | irem | lrem | frem | drem | |||||
Tneg | ineg | lneg | fneg | dneg | |||||
Tshl | ishl | lshl | |||||||
Tshr | ishr | lshr | |||||||
Tushr | iushr | lushr | |||||||
Tand | iand | land | |||||||
Tor | ior | lor | |||||||
Txor | ixor | lxor | |||||||
i2T | i2b | i2s | i2l | i2f | i2d | ||||
l2T | l2i | l2f | l2d | ||||||
f2T | f2i | f2l | f2d | ||||||
d2T | d2i | d2l | d2f | ||||||
Tcmp | lcmp | ||||||||
Tcml | fcml | dcml | |||||||
Tcmpg | fcmpg | dcmpg | |||||||
if_TcmpOP | if_icmpOP | if_acopOP | |||||||
Treturn | ireturn | lreturn | freturn | dreturn | areturn |
大部分指令沒有支持 byte,char 和 short 甚至是 boolean,編譯器會在編譯器或者運行期把這類數據擴展爲 int 類型數據。
加載/存儲指令用於將數據在棧幀中的局部變量表和操做數棧之間來回傳輸。
Tload
, Tload_n
後者表示是一組指令。Tstore
, Tstore_n
後者表示是一組指令。Tipush
, ldc
,T_const
等wide
對操做數棧的數值進行運算以後把結果從新存入操做棧棧頂。
Tadd
Tsub
Tmul
Tdiv
Trem
Tneg
Tshl
, Tshr
, Tushr
Tor
Tand
Txor
Tinc
Tcmpg
,Tcmpl
類型轉化指令用於將兩種不一樣的數值類型進行相互轉換,這種轉換操做通常用於實現用戶代碼中的顯式轉換操做,或者用於處理字節碼指令集中數據類型相關指令沒法與數據類型一一對應的問題。
i2T
l2T
f2T
d2T
儘管類實例和數組都是對象,但Java虛擬機對類實例和數組的建立與操做使用了不一樣的字節碼指令。
new
newarray
, anewarray
, multianewarray
getfield
, putfield
,getstatic
,putstatic
Taload
Tastore
arraylength
instanceof
, checkcast
pop
,pop2
dup
,dup2
, dup_x1
,dup2_x1
,dup_x2
,dup2_x2
swap
讓虛擬機能夠有條件或者無條件地從特定位置指令執行程序而不是在控制轉移指令的下一條指令執行程序。
ifeq
, ifit
, ifle
, ifne
, ifgt
, ifge
, ifull
, ifnonnull
, if_icmpeq
, if_icmpne
, if_icmplt
, if_icmpgt
, if_icmple
, if_icmpge
, if_acmpeq
, if_acmpne
tableswitch
, lookupswitch
goto
, goto_w
, jsr
, jsr_w
, ret
invokevirtual
,根據對象的實際類型進行分派invokeinterface
, 會在運行時搜索一個實現了這個接口的方法的對象,找到適合的方法進行調用invokespecial
,包括實例初始化方法,私有方法和父類方法invokestatic
用於調用static方法invokedynamic
,區別於前面4條指令,它們都在固化在jvm內部,而該指令的分派邏輯是由用戶所設定的引導方法決定的。athrow
指令用於完成顯式拋出異常(throw語句)的操做,除了用throw語句以外,JVM還規定在運行時會在其餘 JVM指令檢測到異常情況的時候自動拋出。好比當除數爲0的時候,JVM會在 idiv
或 ldiv
中拋出 ArithmeticException 異常。
JVM 使用管程(Monitor)來支持如下同步場景:
monitorenter
和 monitorexit
來支持 synchronized 功能。若是你也會用到 Java 字節碼文件結構及指令信息,趕忙收藏起來吧~
專一 Android 進階技術分享,記錄架構師野蠻成長之路
若是在Android領域有遇到任何問題,包括項目遇到的技術問題,面試及簡歷描述問題,亦或對將來職業規劃有疑惑,可添加我微信 「Ming_Lyan」 或關注公衆號 「Android之禪」,會盡自所能和你討論解決。 後續會針對 「Android 領域的必備進階技術」,「Android高可用架構設計及實踐」 ,「業務中的疑難雜症及解決方案」 等實用內容進行分享。 也會分享做爲技術者如何在公司野蠻成長,包括技術進步,職級及收入的提高。 歡迎來撩。