Java字節(jié)碼指令集的使用詳細(xì)
do{
自動(dòng)計(jì)算PC寄存器以及從PC寄存器的位置取出操作碼
if(存在操作數(shù)) 取出操作數(shù);
執(zhí)行操作碼所定義的操作;
}while(處理下一次循環(huán))
操作數(shù)的數(shù)量以及長(zhǎng)度,取決于操作碼,若一個(gè)操作數(shù)長(zhǎng)度超過(guò)了一個(gè)字節(jié),將會(huì)以Big-Endian順序存儲(chǔ)(高位在前字節(jié)碼),其值應(yīng)為(byte1<<8)|byte2。
字節(jié)碼指令流是單字節(jié)對(duì)齊,只有"tableswitch"和"lookupswitch"兩指令例外,它們的操作數(shù)比較特殊,以4字節(jié)為界限劃分的,需要預(yù)留出相應(yīng)的空位來(lái)實(shí)現(xiàn)對(duì)齊。
限制Java虛擬機(jī)操作碼的長(zhǎng)度為一個(gè)字節(jié),且放棄編譯后代碼的參數(shù)長(zhǎng)度對(duì)齊,是為了獲得短小精干的編譯代碼,即使可能會(huì)讓JVM實(shí)現(xiàn)付出一定性能成本為代價(jià)。由于操作碼只能有一個(gè)字節(jié)長(zhǎng)度,故限制了指令集的數(shù)量,又沒有假設(shè)數(shù)據(jù)是對(duì)齊好的,意味著數(shù)據(jù)超過(guò)一個(gè)字節(jié)時(shí),不得不從字節(jié)中重建出具體的數(shù)據(jù)結(jié)構(gòu),會(huì)損失一些性能。
數(shù)據(jù)類型與Java虛擬機(jī)
在JVM中的指令集中,大多數(shù)指令包含了其操作對(duì)應(yīng)的數(shù)據(jù)類型信息。如iload指令從局部變量表中加載int型的數(shù)據(jù)到操作數(shù)棧中,而fload加載的是float類型的數(shù)據(jù)。對(duì)于大部分與數(shù)據(jù)類型相關(guān)的字節(jié)碼指令,他們的操作碼助記符都有特殊的字符來(lái)表明:i代表int類型,l代表long,s代表short,b代表 byte,c代表char,f代表float,d代表double,a代表reference。有一些單獨(dú)指令可以在必要的時(shí)候用來(lái)將一些不不支持的類型轉(zhuǎn)換為可被支持的類型。
加載和存儲(chǔ)指令
加載和存儲(chǔ)指令用于將數(shù)據(jù)從棧幀的局部變量表和操作數(shù)棧之間來(lái)回傳輸。1)將一個(gè)局部變量加載到操作數(shù)棧的指令包括:iload,iload_<n>,lload、lload_<n>、float、 fload_<n>、dload、dload_<n>,aload、aload_<n>。
2)將一個(gè)數(shù)值從操作數(shù)棧存儲(chǔ)到局部變量標(biāo)的指令: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_ml,iconst_<i>,lconst_<l>,fconst_<f>,dconst_<d>
4)局部變量表的訪問索引指令:wide
一部分以尖括號(hào)結(jié)尾的指令代表了一組指令,如iload_<i>,代表了iload_0,iload_1等,這幾組指令都是帶有一個(gè)操作數(shù)的通用指令。
運(yùn)算指令
算術(shù)指令用于對(duì)兩個(gè)操作數(shù)棧上的值進(jìn)行某種特定運(yùn)算,并把結(jié)果重新存入到操作棧頂。1)加法指令:iadd,ladd,fadd,dadd
2)減法指令:isub,lsub,fsub,dsub
3)乘法指令:imul,lmul,fmul,dmul
4)除法指令:idiv,ldiv,fdiv,ddiv
5)求余指令:irem,lrem,frem,drem
6)取反指令:ineg,leng,fneg,dneg
7)位移指令:ishl,ishr,iushr,lshl,lshr,lushr
8)按位或指令:ior,lor
9)按位與指令:iand,land
10)按位異或指令:ixor,lxor
11)局部變量自增指令:iinc
12)比較指令:dcmpg,dcmpl,fcmpg,fcmpl,lcmp
Java虛擬機(jī)沒有明確規(guī)定整型數(shù)據(jù)溢出的情況,但規(guī)定了處理整型數(shù)據(jù)時(shí),只有除法和求余指令出現(xiàn)除數(shù)為0時(shí)會(huì)導(dǎo)致虛擬機(jī)拋出異常。
加載和存儲(chǔ)指令
加載和存儲(chǔ)指令用于將數(shù)據(jù)從哦你哦過(guò)棧幀的局部變量表和操作數(shù)棧之間來(lái)回傳輸。1)將一個(gè)局部變量加載到操作數(shù)棧的指令包括:iload,iload_<n>,lload、lload_<n>、float、 fload_<n>、dload、dload_<n>,aload、aload_<n>。
2)將一個(gè)數(shù)值從操作數(shù)棧存儲(chǔ)到局部變量標(biāo)的指令: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_ml,iconst_<i>,lconst_<l>,fconst_<f>,dconst_<d>
4)局部變量表的訪問索引指令:wide
一部分以尖括號(hào)結(jié)尾的指令代表了一組指令,如iload_<i>,代表了iload_0,iload_1等,這幾組指令都是帶有一個(gè)操作數(shù)的通用指令。
運(yùn)算指令
算術(shù)指令用于對(duì)兩個(gè)操作數(shù)棧上的值進(jìn)行某種特定運(yùn)算,并把結(jié)果重新存入到操作棧頂。1)加法指令:iadd,ladd,fadd,dadd
2)減法指令:isub,lsub,fsub,dsub
3)乘法指令:imul,lmul,fmul,dmul
4)除法指令:idiv,ldiv,fdiv,ddiv
5)求余指令:irem,lrem,frem,drem
6)取反指令:ineg,leng,fneg,dneg
7)位移指令:ishl,ishr,iushr,lshl,lshr,lushr
8)按位或指令:ior,lor
9)按位與指令:iand,land
10)按位異或指令:ixor,lxor
11)局部變量自增指令:iinc
12)比較指令:dcmpg,dcmpl,fcmpg,fcmpl,lcmp
Java虛擬機(jī)沒有明確規(guī)定整型數(shù)據(jù)溢出的情況,但規(guī)定了處理整型數(shù)據(jù)時(shí),只有除法和求余指令出現(xiàn)除數(shù)為0時(shí)會(huì)導(dǎo)致虛擬機(jī)拋出異常。
類型轉(zhuǎn)換指令
類型轉(zhuǎn)換指令將兩種Java虛擬機(jī)數(shù)值類型相互轉(zhuǎn)換,這些操作一般用于實(shí)現(xiàn)用戶代碼的顯式類型轉(zhuǎn)換操作。JVM支持寬化類型轉(zhuǎn)換(小范圍類型向大范圍類型轉(zhuǎn)換):
1)int類型到long,float,double類型
2)long類型到float,double類型
3)float到double類型
窄花類型轉(zhuǎn)換指令:i2b,i2c,i2s,l2i,f2i,f2l,d2l和d2f,窄化類型轉(zhuǎn)換可能會(huì)導(dǎo)致轉(zhuǎn)換結(jié)果產(chǎn)生不同的正負(fù)號(hào),不同數(shù)量級(jí),轉(zhuǎn)換過(guò)程可能會(huì)導(dǎo)致數(shù)值丟失精度。如int或long類型轉(zhuǎn)化整數(shù)類型T時(shí),轉(zhuǎn)換過(guò)程是僅僅丟棄最低位N個(gè)字節(jié)意外的內(nèi)容(N是類型T的數(shù)據(jù)類型長(zhǎng)度)
對(duì)象創(chuàng)建與操作
雖然類實(shí)例和數(shù)組都是對(duì)象,Java虛擬機(jī)對(duì)類實(shí)例和數(shù)組的創(chuàng)建與操作使用了不同的字節(jié)碼指令。1)創(chuàng)建實(shí)例的指令:new
2)創(chuàng)建數(shù)組的指令:newarray,anewarray,multianewarray
3)訪問字段指令:getfield,putfield,getstatic,putstatic
4)把數(shù)組元素加載到操作數(shù)棧指令:baload,caload,saload,iaload,laload,faload,daload,aaload
5)將操作數(shù)棧的數(shù)值存儲(chǔ)到數(shù)組元素中執(zhí)行:bastore,castore,castore,sastore,iastore,fastore,dastore,aastore
6)取數(shù)組長(zhǎng)度指令:arraylength
7)檢查實(shí)例類型指令:instanceof,checkcast
操作數(shù)棧管理指令
直接操作操作數(shù)棧的指令:pop,pop2,dup,dup2,dup_x1,dup2_x1,dup_x2,dup2_x2和swap控制轉(zhuǎn)移指令
讓JVM有條件或無(wú)條件從指定指令而不是控制轉(zhuǎn)移指令的下一條指令繼續(xù)執(zhí)行程序。控制轉(zhuǎn)移指令包括:
1)條件分支:ifeq,iflt,ifle,ifne,ifgt,ifge,ifnull,ifnotnull,if_cmpeq,if_icmpne,if_icmlt,if_icmpgt等
2)復(fù)合條件分支:tableswitch,lookupswitch
3)無(wú)條件分支:goto,goto_w,jsr,jsr_w,ret
JVM中有專門的指令集處理int和reference類型的條件分支比較操作,為了可以無(wú)明顯標(biāo)示一個(gè)實(shí)體值是否是null,有專門的指令檢測(cè)null 值。boolean類型和byte類型,char類型和short類型的條件分支比較操作,都使用int類型的比較指令完成,而 long,float,double條件分支比較操作,由相應(yīng)類型的比較運(yùn)算指令,運(yùn)算指令會(huì)返回一個(gè)整型值到操作數(shù)棧中,隨后再執(zhí)行int類型的條件比較操作完成整個(gè)分支跳轉(zhuǎn)。各種類型的比較都最終會(huì)轉(zhuǎn)化為int類型的比較操作。
方法調(diào)用和返回指令
invokevirtual指令:調(diào)用對(duì)象的實(shí)例方法,根據(jù)對(duì)象的實(shí)際類型進(jìn)行分派(虛擬機(jī)分派)。invokeinterface指令:調(diào)用接口方法,在運(yùn)行時(shí)搜索一個(gè)實(shí)現(xiàn)這個(gè)接口方法的對(duì)象,找出合適的方法進(jìn)行調(diào)用。
invokespecial:調(diào)用需要特殊處理的實(shí)例方法,包括實(shí)例初始化方法,私有方法和父類方法
invokestatic:調(diào)用類方法(static)
方法返回指令是根據(jù)返回值的類型區(qū)分的,包括ireturn(返回值是boolean,byte,char,short和 int),lreturn,freturn,drturn和areturn,另外一個(gè)return供void方法,實(shí)例初始化方法,類和接口的類初始化i 方法使用。
同步
JVM支持方法級(jí)同步和方法內(nèi)部一段指令序列同步,這兩種都是通過(guò)moniter實(shí)現(xiàn)的。方法級(jí)的同步是隱式的,無(wú)需通過(guò)字節(jié)碼指令來(lái)控制,它實(shí)現(xiàn)在方法調(diào)用和返回操作中。虛擬機(jī)從方法常量池中的方法標(biāo)結(jié)構(gòu)中的 ACC_SYNCHRONIZED標(biāo)志區(qū)分是否是同步方法。方法調(diào)用時(shí),調(diào)用指令會(huì)檢查該標(biāo)志是否被設(shè)置,若設(shè)置,執(zhí)行線程持有moniter,然后執(zhí)行方法,最后完成方法時(shí)釋放moniter。
同步一段指令集序列,通常由synchronized塊標(biāo)示,JVM指令集中有monitorenter和monitorexit來(lái)支持synchronized語(yǔ)義。
結(jié)構(gòu)化鎖定是指方法調(diào)用期間每一個(gè)monitor退出都與前面monitor進(jìn)入相匹配的情形。JVM通過(guò)以下兩條規(guī)則來(lái)保證結(jié)結(jié)構(gòu)化鎖成立(T代表一線程,M代表一個(gè)monitor):
1)T在方法執(zhí)行時(shí)持有M的次數(shù)必須與T在方法完成時(shí)釋放的M次數(shù)相等
2)任何時(shí)刻都不會(huì)出現(xiàn)T釋放M的次數(shù)比T持有M的次數(shù)多的情況
相關(guān)文章
java中并發(fā)Queue種類與各自API特點(diǎn)以及使用場(chǎng)景說(shuō)明
這篇文章主要介紹了java中并發(fā)Queue種類與各自API特點(diǎn)以及使用場(chǎng)景說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06Java 基礎(chǔ)之NIO 學(xué)習(xí)詳解
這篇文章主要介紹了java基礎(chǔ)之NIO介紹及使用,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java基礎(chǔ)的小伙伴們有非常好的幫助,需要的朋友可以參考下2021-09-09JSP頁(yè)面pageEncoding和contentType屬性
有關(guān)于JSP頁(yè)面中pageEncoding和contentType屬性。2013-04-04Java代碼實(shí)現(xiàn)微信頁(yè)面滾動(dòng)防露底(核心代碼)
這篇文章主要介紹了Java代碼實(shí)現(xiàn)微信頁(yè)面滾動(dòng)防露底的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-09-09spring boot jar部署控制臺(tái)日志亂碼的解決
這篇文章主要介紹了spring boot jar部署控制臺(tái)日志亂碼的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09