JVM - 字節(jié)碼指令集

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容