Java虛擬機(jī)的指令由一個字節(jié)長度的、代表著某種特定操作含義的操作碼(Opcode)以及跟隨其后的零至多個代表此操作所需參數(shù)的操作數(shù)(Operands)所構(gòu)成。虛擬機(jī)中許多指令并不包含操作數(shù),只有一個操作碼。
對于大部分與數(shù)據(jù)類型相關(guān)的字節(jié)碼指令,他們的操作碼助記符中都有特殊的字符來表明專門為哪種類型服務(wù):
- i代表對int類型的操作
- l代long
- s代表short
- b代表byte
- c代表char
- f代表float
- d代表double
- a代表reference
也有一些指令的助記符中沒有明確地指明操作類型的字母,例如arraylength指令,它沒有代表數(shù)據(jù)類型的特殊字符,但操作數(shù)永遠(yuǎn)只能是一個數(shù)組類型的對象。
還有另外一些指令,例如無條件跳轉(zhuǎn)指令goto則是與數(shù)據(jù)類型無關(guān)的。
加載和存儲指令
加載和存儲指令用于將數(shù)據(jù)從棧幀的局部變量表和操作數(shù)棧之間來回傳輸。
| 1. 將一個局部變量加載到操作棧的指令 |
|---|
| iload、iload_<n>、lload、lload_<n>、fload、fload_<n>、dload、dload_<n>、aload、aload_<n> |
| 2. 將一個數(shù)值從操作數(shù)棧存儲到局部變量表的指令 |
|---|
| istore、istore_<n>、lstore、lstore_<n>、fstore、fstore_<n>、dstore、dstore_<n>、astore、astore_<n> |
| 3. 將一個常量加載到操作數(shù)棧的指令 |
|---|
| bipush、sipush、ldc、ldc_w、ldc2_w、aconst_null、iconst_m1、iconst_<i>、lconst_<l>、fconst_<f>、dconst_<d> |
| 4. 擴(kuò)充局部變量表的訪問索引的指令 |
|---|
| wide |
上述所列舉的指令助記符中,有一部分是以尖括號結(jié)尾的(例如iload_<n>),這些指令助記符實際上是代表了一組指令(例如iload_<n>,它代表了iload_0、iload_1、iload_2和iload_3這幾條指令)。這幾條指令都是某個帶有一個操作數(shù)的通用指令(例如iload)的特殊指令,對于這若干組特殊指令來說,它們表面上沒有操作數(shù),不需要進(jìn)行取操作數(shù)的操作,但操作數(shù)都是指令隱含的。除此之外,他們的語義與原生的通用指令完全一致(例如iload_0的語義與操作數(shù)為0時的iload指令語義完全一致)。
運(yùn)算指令
算術(shù)指令用于對兩個操作數(shù)棧上的值進(jìn)行某種特定運(yùn)算,并把結(jié)果重新存入到操作棧頂。大體運(yùn)算指令可以分為兩種:對整型數(shù)據(jù)進(jìn)行運(yùn)算的指令與對浮點(diǎn)型數(shù)據(jù)進(jìn)行運(yùn)算的指令,無論是那種算術(shù)指令,都是使用Java虛擬機(jī)的數(shù)字類型的。數(shù)據(jù)沒有直接支持byte、short、char和boolean類型的算術(shù)指令,對于這些數(shù)據(jù)的運(yùn)算,都是使用操作int類型的指令。整數(shù)與浮點(diǎn)數(shù)的算術(shù)指令在溢出和被零除的時候也有各自不同的行為,所有的算術(shù)指令包括:
- 加法指令: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
類型轉(zhuǎn)換指令
類型轉(zhuǎn)換指令可以將兩種Java虛擬機(jī)數(shù)值類型進(jìn)行相互轉(zhuǎn)換,這些轉(zhuǎn)換操作一般用于實現(xiàn)用戶代碼的顯示類型轉(zhuǎn)換操作,或者用來處理Java虛擬機(jī)字節(jié)碼指令集中指令非完全獨(dú)立的問題。
Java虛擬機(jī)直接支持(“直接支持”意味著轉(zhuǎn)換時無需顯示的轉(zhuǎn)換指令)以下數(shù)值的寬化類型轉(zhuǎn)換(Widening Numeric Conversions,小范圍類型向大范圍類型的安全轉(zhuǎn)換):
- int類型到long、float或者double類型
- long類型到float、double類型
- float類型到double類型
窄化類型轉(zhuǎn)換(Narrowing Numeric Conversions)指令包括有: i2b、i2c、i2s、l2i、f2i、f2l、d2i、d2l和d2f。
對象創(chuàng)建與操作
雖然類實例和數(shù)組都是對象,但Java虛擬機(jī)對類實例和數(shù)組的創(chuàng)建與操作使用了不同的字節(jié)碼指令:
- 創(chuàng)建類實例的指令:new
- 創(chuàng)建數(shù)組實例的指令:newarray,anewarray,multianewarray
- 訪問類字段(static字段,或稱為類變量)和實例字段(非static字段,或者稱為實例變量)的指令:getstatic、putstatic、getfield、putfield
- 把一個數(shù)組元素加載到操作數(shù)棧的指令:baload、caload、saload、iaload、laload、faload、daload、aaload
- 將一個操作數(shù)棧的值存儲到數(shù)組元素的指令:bastore、castore、sastore、iastore、fastore、dastore、aastore
- 取數(shù)組長度的指令:arraylength
- 檢查類實例類型的指令:instanceof、checkcast
操作數(shù)棧管理指令
Java虛擬機(jī)提供了一些用于直接操作操作數(shù)棧的指令,包括:pop、pop2、dup、dup2、dup_x1、dup2_x1、dup_x2、dup2_x2和swap。
控制轉(zhuǎn)移指令
控制轉(zhuǎn)移指令可以讓Java虛擬機(jī)有條件或無條件地從指定指令而不是控制轉(zhuǎn)移指令的下一條指令繼續(xù)執(zhí)行程序。控制轉(zhuǎn)移指令包括有:
- 條件分支:ifeq、iflt、ifle、ifne、ifgt、ifge、ifnull、ifnonnull、if_icmpeq、if_icmpne、if_icmplt、if_icmpgt、if_icmple、if_icmpge、if_acmpeq和if_acmpne。
- 復(fù)合條件分支:tableswitch、lookupswitch
- 無條件分支:goto、goto_w、jsr、jsr_w、ret
方法調(diào)用和返回指令
以下四條指令用于方法調(diào)用:
| 指令 | 作用 |
|---|---|
| invokevirtual | 用于調(diào)用對象的實例方法,根據(jù)對象的實際類型進(jìn)行分派(虛方法分派),這也是Java語言中最常見的方法分派方式 |
| invokeinterface | 用于調(diào)用接口方法,它會在運(yùn)行時搜索一個實現(xiàn)了這個接口方法的對象,找出適合的方法進(jìn)行調(diào)用 |
| invokespecial | 用于調(diào)用一些需要特殊處理的實例方法,包括實例初始化方法、私有方法和父類方法 |
| invokestatic | 用于調(diào)用類方法(static方法) |
拋出異常
在程序中顯示拋出異常的操作會由athrow指令實現(xiàn),除了這種情況,還有別的異常會在其他Java虛擬機(jī)指令檢測到異常情況時由虛擬機(jī)自動拋出。
同步
monitorenter和monitorexit