JVM 位元組碼指令
阿新 • • 發佈:2021-01-08
> 本文部分摘自《深入理解 Java 虛擬機器》
## 簡介 Java 虛擬機器的指令由操作碼 + 運算元組成,其中操作碼是代表某種特定操作含義的數字,長度為一個位元組,而運算元就是此操作所需的一個或多個引數。由於 Java 虛擬機器採用面向運算元棧而非暫存器的架構,所以大多數指令都不包括運算元,只有一個操作碼 既然限制了 JVM 操作碼的長度為一個位元組(0 ~ 255),也意味著指令集的操作碼總數不超過 256 條。Class 檔案格式放棄了編譯後代碼的運算元長度對齊,因此虛擬機器在處理那些超過一個位元組的資料時,不得不在執行時從位元組中重建出具體資料的結構,這會損失一些效能,但也省略了大量的填充和間隔符號,儘可能得到短小精悍的編譯程式碼
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 | |||||||
Tcmpl | fcmpl | dcmpl | ||||||
Tcmpg | fcmpg | dcmpg | ||||||
if_TcmpOP | if_icmpOP | if_acmpOP | ||||||
Treturn | ireturn | lreturn | freturn | dreturn | areturn |
## 載入和儲存指令 載入和儲存指令用於將資料在棧幀中的區域性變數和運算元棧之間來回傳輸,這類指令包括: - 將一個區域性變數載入到運算元棧:iload、iload\_\
## 運算指令 算術指令用於對兩個運算元棧上的值進行某種特定運算,並把結果重新存入到運算元棧頂。所有的算術指令包括: - 加法指令:iadd、ladd、fadd、dadd - 減法指令:isub、lsub、fsub、dsub - 乘法指令:imul、lmul、fmul、dmul - 除法指令:idiv、ldiv、fdiv、ddiv - 求餘指令:irem、lrem、frem、drem - 取反指令:ineg、lneg、fneg、dneg - 位移指令:ishl、ishr、iushr、lshl、lshr、lushr - 按位或指令:ior、lor - 按位與指令:iand、land - 按位異或指令:ixor、lxor - 區域性變數自增指令:iinc - 比較指令:dcmpg、dcmpl、fcmpg、fcmpl、lcmp
## 型別轉換指令 型別轉換指令可以將兩種不同的數值型別相互轉換,這些轉換操作一般用於實現使用者程式碼中的顯式型別轉換操作,或者用於開篇所提到的位元組碼指令集中資料型別相關指令與資料型別一一對應的問題 Java 支援小範圍型別向大範圍型別的安全轉換,例如 int 到 long、float、double,與之相反的就必須顯式地使用轉換指令完成,這些指令包括 i2b、i2c、i2s、l2i、f2i、f2l、d2i、d2l、d2f 轉換過程可能會導致數值的精度丟失
## 物件建立與訪問指令 雖然類例項和陣列都是物件,但 Java 虛擬機器對類例項和陣列的建立與操作使用了不同的位元組碼指令。物件建立後,就可以通過物件訪問指令獲取物件例項或者陣列例項中的欄位或者陣列元素: - 建立類例項指令:new - 建立陣列的指令:newarray、anewarray、multianewarray - 訪問類欄位(static 欄位、或者稱為類變數)和例項欄位(非 static 欄位,或被稱為例項變數)的指令:getfield、putfield、getstatic、putstatic - 把一個數組元素載入到運算元棧的指令:baload、caload、saload、iaload、laload、faload、daload、aaload - 將一個運算元棧的值儲存到陣列元素中的指令:bastore、castore、sastore、iastore、fastore、dastore、aastore - 取陣列長度的指令:arraylength - 檢查類例項型別的指令:instanceof、checkcast
## 運算元棧管理指令 如同操作一個普通資料結構中的堆疊那樣,Java 虛擬機器提供了一些用於直接操作運算元棧的指令,包括: - 將運算元棧的棧頂一個或兩個元素出棧:pop、pop2 - 複製棧頂一個或兩個陣列並將複製值或雙值的複製值重新壓入棧頂:dup、dup2、dup_x1、dup2_x1、dup_x2、dup2_x2 - 將棧最頂端的兩個數值互換:swap
## 控制轉移指令 控制轉移指令可以讓 Java 虛擬機器有條件或無條件地從指定位置指令的下一條指令繼續執行程式,從概念模型上理解,可以認為控制指令就是在有條件或無條件地修改 PC 暫存器的值: - 條件分支:ifeq、iflt、ifle、ifne、ifgt、ifnull、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 指令:用於呼叫類靜態方法 - invokedynamic 指令:用於在執行時動態解析出呼叫點限定符所引用的方法,並執行
## 異常處理指令 在 Java 程式中顯式地丟擲異常的操作(throw)都由 athrow 指令來實現,除了用 throw 語句顯式丟擲異常的情況外,Java虛擬機器規範還規定了許多執行時異常會在其他 Java 虛擬機器指令檢測到異常狀況時自動丟擲。對於處理異常(catch)操作,不是由位元組碼指令來實現,而是採用異常表
## 同步指令 Java 虛擬機器可以支援方法級的同步和方法內部一段指令序列的同步,這兩種同步結構都使用管程(Monitor,更常見的是直接稱它為鎖)來實現 方法級的同步是隱式的,無須通過位元組碼指令是實現,它實現在方法呼叫和返回操作之中。虛擬機器可以從方法常量池中的方法表結構中的 ACC_SYNCHRONIZED 訪問標誌得知一個方法是否被宣告為同步方法。當方法被呼叫時,呼叫指令會檢查方法的 ACC_SYNCHRONIZED 訪問標誌是否被設定,如果設定了,執行執行緒就要去先成功持有管程。在方法執行期間,執行執行緒持有管程,其他任何執行緒都無法再獲取到同一個管程。如果一個同步方法執行期間丟擲異常,並在方法內部無法處理,此時同步方法所持有的管程將在異常拋到同步方法邊界之外自動釋放 同步一段指令集序列通常是由 Java 語言中的 synchronized 語句塊來表示的,Java 虛擬機器的指令集中有 monitorenter 和 monitorexit 兩條指令來支援 synchronized 關鍵字的語義,兩條指令之間包裹需要同步的指令序列,以實現同步效