Java字節碼「隨身手冊」

隨身手冊

在 Android 編程中,若是要閱讀及修改字節碼,則須要針對 Class文件 結構進行解析翻譯。 JVM 定義 Class文件 結構及指令集,經過查閱字節碼錶及指令集可瞭解 class文件 的內容邏輯。下面經過整理涉及的全部表格內容及指令,供查詢使用。html

分有兩大部份內容:Class 文件結構描述表字節碼指令表java

表格參考來自於 「深刻理解Java虛擬機:JVM高級特定及最佳實踐」

Class 文件結構描述表

Class文件Java文件 編譯而來,以 JVM 定義的特定結構來描述文件定義的內容。主要表格分類爲:面試

  • Class 文件結構表
  • 常量表
  • 訪問標誌表
  • 字段表
  • 方法表
  • 屬性表
  • 特殊字符串表

Class文件結構表

類型 名稱 描述 數量
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

常量表

常量池主要存放兩種類型:編程

  • 字面量,包含文本字符串,final的常量值等
  • 符號引用,類和接口的全限定名,字段的名稱和描述符,方法的名稱和描述符

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結構,表示方法名和方法描述符

訪問標誌表

訪問標誌表根據如下不一樣標誌類型進一步劃分:數組

  1. 類訪問標誌
  2. 內部類訪問標
  3. 字段訪問標誌
  4. 方法訪問標誌

類訪問標誌

用於識別一些類或者接口層次的訪問信息,包括這個 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) 用於標示屬性值所佔用的位數,後面再跟着屬性內容。

Code屬性結構

類型 名稱 數量
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

Exceptions屬性結構

區別與異常表,該表主要是列舉中方法中可能拋出的受檢查異常,也就是方法描述時throws關鍵字列舉的異常

類型 名稱 數量
u2 attribute_name_index 1
u4 attribute_length 1
u2 number_of_exceptions 1
u2 exception_index_table number_of_exceptions

LineNumberTable屬性結構

用於描述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類型的數據項。

LocalVariableTable屬性結構

用於描述棧幀中局部變量表中的變量與 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

SourceFile屬性結構

用於記錄生成該 Class文件 的源碼文件名稱。

類型 名稱 數量
u2 attribute_name_index 1
u4 attribute_length 1
u2 sourcefile_index 1

其中 sourcefile_index 爲指向常量池 CONSTANT_Utf8_info 索引。

ConstantValue屬性結構

用於通知虛擬機自動爲靜態變量賦值。只有被 static 關鍵字修飾的變量纔可使用這項屬性。

類型 名稱 數量
u2 attribute_name_index 1
u4 attribute_length 1
u2 constant_index 1

InnerClasses屬性結構

用於記錄內部類與宿主類之間的關聯。若是一個類中定義了內部類,編譯器則會爲它生成內部類 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

Deprecated/Synthetic屬性結構

前者是用於標示某個類,字段或者方法是否再也不推薦使用。

後者是用於標示字段或者方法不是由 Java 源碼直接產生。全部由非用戶代碼生成的方法都須要設置 Synthetic 屬性或者 ACC_SYNTHETIC 標誌,可是 <init><clinit> 除外。他們的結構以下:

類型 名稱 數量
u2 attribute_name_index 1
u4 attribute_length 1

StackMapTable屬性結構

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

Signature屬性結構

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屬性是字段表的屬性,則表示字段類型簽名。

BootstrapMethods屬性結構

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 類型數據。

加載/存儲指令

加載/存儲指令用於將數據在棧幀中的局部變量表和操做數棧之間來回傳輸。

  • 將一個局部變量加載到操做棧: TloadTload_n 後者表示是一組指令。
  • 將一個數值從操做數棧存儲到局部變量表: TstoreTstore_n 後者表示是一組指令。
  • 將一個常量加載到操做數棧: TipushldcT_const
  • 擴充局部變量表的訪問索引指令:wide

運算指令

對操做數棧的數值進行運算以後把結果從新存入操做棧棧頂。

  • 加法指令 Tadd
  • 減法指令 Tsub
  • 乘法指令 Tmul
  • 除法指令 Tdiv
  • 求餘指令 Trem
  • 取反指令 Tneg
  • 位移指令 Tshl, Tshr, Tushr
  • 按位或指令 Tor
  • 按位與指令 Tand
  • 按位異或指令 Txor
  • 局部變量自增指令 Tinc
  • 比較指令 Tcmpg ,Tcmpl

類型轉化指令

類型轉化指令用於將兩種不一樣的數值類型進行相互轉換,這種轉換操做通常用於實現用戶代碼中的顯式轉換操做,或者用於處理字節碼指令集中數據類型相關指令沒法與數據類型一一對應的問題。

  • int類型轉其餘 i2T
  • long類型轉其餘 l2T
  • float類型轉其餘 f2T
  • double類型轉其餘 d2T

對象建立與訪問指令

儘管類實例和數組都是對象,但Java虛擬機對類實例和數組的建立與操做使用了不一樣的字節碼指令。

  • 建立類實例 new
  • 建立數組 newarray, anewarray, multianewarray
  • 訪問類變量和實例變量 getfield, putfieldgetstaticputstatic
  • 把一個數組元素加載到操做數棧 Taload
  • 將一個操做數棧的值存儲到數組元素中 Tastore
  • 取數組長度的指令 arraylength
  • 檢查類實例類型 instanceof, checkcast

操做數棧管理指令

  • 將操做數棧棧頂一個或者兩個元素出棧 poppop2
  • 複製棧頂一個或兩個數值並將複製值從新壓入棧頂 dupdup2, dup_x1dup2_x1,dup_x2dup2_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會在 idivldiv 中拋出 ArithmeticException 異常。

同步指令

JVM 使用管程(Monitor)來支持如下同步場景:

  • 方法級的同步,不須要字節碼控制,實現於方法調用和返回操做志宏。從方法表中 ACC_SYNCHRONIZED 獲得一個方法是不是同步,若是被設置,則執行線程須要先持有管程才能執行,執行完以後釋放管程。
  • 方法內部一段指令序列的同步,由monitorentermonitorexit來支持 synchronized 功能。
若是你也會用到 Java 字節碼文件結構及指令信息,趕忙收藏起來吧~

專一 Android 進階技術分享,記錄架構師野蠻成長之路

若是在Android領域有遇到任何問題,包括項目遇到的技術問題,面試及簡歷描述問題,亦或對將來職業規劃有疑惑,可添加我微信 「Ming_Lyan」 或關注公衆號 「Android之禪」,會盡自所能和你討論解決。 後續會針對 「Android 領域的必備進階技術」,「Android高可用架構設計及實踐」 ,「業務中的疑難雜症及解決方案」 等實用內容進行分享。 也會分享做爲技術者如何在公司野蠻成長,包括技術進步,職級及收入的提高。 歡迎來撩。
相關文章
相關標籤/搜索