JVM004_字节码指令简介
字節(jié)碼指令簡(jiǎn)介
Java虛擬機(jī)指令由操作碼(Opcode)和跟隨其后的零至多個(gè)操作數(shù)(Operand)組成。
-
操作碼:一個(gè)字節(jié)長(zhǎng)度的,代表某種特定操作含義的數(shù)字。
-
操作數(shù):操作碼需要的參數(shù)。
字節(jié)碼與數(shù)據(jù)類型
| 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 | astore |
| 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_TempOP | if_iempOP | if_aempOP | ||||||
| Treturn | ireturn | lreturn | freturn | dreturn | areturn |
大部分指令都沒有支持整數(shù)類型byte、 char和short, 沒有任何指令支持boolean類型。 編譯器會(huì)在編譯期或運(yùn)行期將byte和short類型的數(shù)據(jù)帶符號(hào)擴(kuò)展(Sign-Extend) 為相應(yīng)的int類型數(shù)據(jù), 將boolean和char類型數(shù)據(jù)零位擴(kuò)展(Zero-Extend) 為相應(yīng)的int類型數(shù)據(jù)。 與之類似, 在處理boolean、 byte、 short和char類型的數(shù)組時(shí), 也會(huì)轉(zhuǎn)換為使用對(duì)應(yīng)的int類型的字節(jié)碼指令來處理。 因此, 大多數(shù)對(duì)于boolean、 byte、 short和char類型數(shù)據(jù)的操作, 實(shí)際上都是使用相應(yīng)的對(duì)int類型作為運(yùn)算類型(Computational Type) 來進(jìn)行的。
加載和存儲(chǔ)指令
加載和存儲(chǔ)指令用于將數(shù)據(jù)在棧幀中的局部變量表和操作數(shù)棧之間來回傳輸 。
-
將一個(gè)局部變量加載到操作棧:iload,iload_0,iload_1,iload_2,iload_3,lload,lload_0,lload_1,lload_2,lload_3,fload_0,fload_1,fload_2,fload_3,dload,dload_0,dload_1,dload_2,dload_3,aload,aload_0,aload_1,aload_2,aload_3
iload_0 表示將第0個(gè)變量槽中的int型變量推到操作數(shù)棧棧頂。
iload_1 表示將第1個(gè)變量槽中的int型變量推到操作數(shù)棧棧頂。
iload 后面會(huì)跟一個(gè)參數(shù)n,表示將第n個(gè)變量槽中的int行變量推送到操作數(shù)棧棧頂。
其余的指令同理。
-
將一個(gè)數(shù)值從操作數(shù)棧存儲(chǔ)到局部變量表中:
istore、 istore_<n>、 lstore、 lstore_<n>、 fstore、fstore_<n>、 dstore、 dstore_<n>、 astore、 astore_<n> -
將一個(gè)常量加載到操作數(shù)棧:
bipush、 sipush、 ldc、 ldc_w、 ldc2_w、 aconst_null、 iconst_m1、iconst_<i>、 lconst_<l>、 fconst_<f>、 dconst_<d> -
擴(kuò)充局部變量表的訪問索引的指令
wide #拓展本地變量的寬度,用于修改其他指令行為?
public class Test2{public int inc(int a,int b,int c,int d,int e,int f,int h,int j,int k){int z = a + 9;return z;} }利用javac -g Test2.java編譯該源文件,再利用javap -v Test2.class反編譯:
public int inc(int, int, int, int, int, int, int, int, int);descriptor: (IIIIIIIII)Iflags: ACC_PUBLICCode:stack=2, locals=11, args_size=100: iload_1 # 將第1個(gè)變量槽中的int型變量推送到棧頂,即將變量a推送到棧頂1: bipush 9 # 將int型常量9推送到棧頂3: iadd # 將棧頂?shù)膬蓚€(gè)int型數(shù)值相加并壓入棧頂,即a+94: istore 10 # 將棧頂int型數(shù)值存入指定的本地變量槽,及將a+9的結(jié)果賦值給變量Z6: iload 10 # 將第10個(gè)變量槽中的變量推送值棧頂8: ireturn # 彈棧,從當(dāng)前方法返回int,及return z;LineNumberTable:line 3: 0line 4: 6LocalVariableTable:Start Length Slot Name Signature0 9 0 this LTest2;0 9 1 a I0 9 2 b I0 9 3 c I0 9 4 d I0 9 5 e I0 9 6 f I0 9 7 h I0 9 8 j I0 9 9 k I6 3 10 z I
運(yùn)算指令
運(yùn)算指令用于將操作數(shù)棧上的兩個(gè)值進(jìn)行某種特定的運(yùn)算,并將結(jié)果重新壓入操作棧頂。可分為對(duì)整型數(shù)據(jù)進(jìn)行運(yùn)算的指令和對(duì)浮點(diǎn)型數(shù)據(jù)進(jìn)行運(yùn)算指令。
不存在直接支持byte、 short、 char和boolean類型的算術(shù)指令 ,而是使用操作int類型的指令代替。
#加法指令: 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)換指令可以將兩種不同的數(shù)值類型相互轉(zhuǎn)換 。
**寬化類型轉(zhuǎn)換:**小范圍類型向大范圍類型的安全轉(zhuǎn)換。
以下數(shù)值類型的寬化類型轉(zhuǎn)換無序顯示的轉(zhuǎn)換指令:
1. int到long,float,double2. long到float,double3. float到double窄化類型轉(zhuǎn)換:窄化類型轉(zhuǎn)換必須顯示的使用轉(zhuǎn)換指令來完成
i2b、i2c、i2s、l2i、f2i、f2l、d2i、d2l、d2f窄化轉(zhuǎn)換可能會(huì)導(dǎo)致轉(zhuǎn)換結(jié)果產(chǎn)生不同的正負(fù)號(hào)、不同的數(shù)量級(jí)的情況,很可能會(huì)出現(xiàn)精度丟失。
JVM將浮點(diǎn)值轉(zhuǎn)換為int或者long時(shí),需要遵循以下規(guī)則:
- 如果浮點(diǎn)值是NaN, 那轉(zhuǎn)換結(jié)果就是int或long類型的0。
- 如果浮點(diǎn)值不是無窮大的話, 浮點(diǎn)值使用IEEE 754的向零舍入模式取整, 獲得整數(shù)值v。 如果v在目標(biāo)類型T(int或long) 的表示范圍之類, 那轉(zhuǎn)換結(jié)果就是v; 否則, 將根據(jù)v的符號(hào), 轉(zhuǎn)換為T所能表示的最大或者最小正數(shù)。
細(xì)節(jié)參看:IEEE 754
對(duì)象創(chuàng)建與訪問指令
·創(chuàng)建類實(shí)例的指令: new ·創(chuàng)建數(shù)組的指令: newarray、 anewarray、 multianewarray ·訪問類字段(static字段, 或者稱為類變量) 和實(shí)例字段(非static字段, 或者稱為實(shí)例變量)的指令: getfield、 putfield、 getstatic、 putstatic ·把一個(gè)數(shù)組元素加載到操作數(shù)棧的指令: baload、 caload、 saload、 iaload、 laload、 faload、daload、 aaload ·將一個(gè)操作數(shù)棧的值儲(chǔ)存到數(shù)組元素中的指令: bastore、 castore、 sastore、 iastore、 fastore、dastore、 aastore ·取數(shù)組長(zhǎng)度的指令: arraylength ·檢查類實(shí)例類型的指令: instanceof、 checkcast操作數(shù)棧管理指令
·將操作數(shù)棧的棧頂一個(gè)或兩個(gè)元素出棧: pop、 pop2 ·復(fù)制棧頂一個(gè)或兩個(gè)數(shù)值并將復(fù)制值或雙份的復(fù)制值重新壓入棧頂: dup、 dup2、 dup_x1、dup2_x1、 dup_x2、 dup2_x2 ·將棧最頂端的兩個(gè)數(shù)值互換: swap控制轉(zhuǎn)移指令
控制轉(zhuǎn)移指令可以讓Java虛擬機(jī)有條件或無條件地從指定位置指令(而不是控制轉(zhuǎn)移指令) 的下一條指令繼續(xù)執(zhí)行程序, 從概念模型上理解, 可以認(rèn)為控制指令就是在有條件或無條件地修改PC寄存器的值。
·條件分支: 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)用和返回指令
·invokevirtual指令: 用于調(diào)用對(duì)象的實(shí)例方法, 根據(jù)對(duì)象的實(shí)際類型進(jìn)行分派(虛方法分派) ,這也是Java語言中最常見的方法分派方式。 ·invokeinterface指令: 用于調(diào)用接口方法, 它會(huì)在運(yùn)行時(shí)搜索一個(gè)實(shí)現(xiàn)了這個(gè)接口方法的對(duì)象, 找出適合的方法進(jìn)行調(diào)用。 ·invokespecial指令: 用于調(diào)用一些需要特殊處理的實(shí)例方法, 包括實(shí)例初始化方法、 私有方法和父類方法。 ·invokestatic指令: 用于調(diào)用類靜態(tài)方法(static方法) 。 ·invokedynamic指令: 用于在運(yùn)行時(shí)動(dòng)態(tài)解析出調(diào)用點(diǎn)限定符所引用的方法。 并執(zhí)行該方法。 前面四條調(diào)用指令的分派邏輯都固化在Java虛擬機(jī)內(nèi)部, 用戶無法改變, 而invokedynamic指令的分派邏輯是由用戶所設(shè)定的引導(dǎo)方法決定的異常處理指令
athrow處理異常是由異常表來完成的,而非字節(jié)碼指令。
同步指令
Java虛擬機(jī)可以支持方法級(jí)的同步和方法內(nèi)部一段指令序列的同步, 這兩種同步結(jié)構(gòu)都是使用管程(Monitor, 更常見的是直接將它稱為“鎖”) 來實(shí)現(xiàn)的。
方法級(jí)的同步是隱式的, 無須通過字節(jié)碼指令來控制, 它實(shí)現(xiàn)在方法調(diào)用和返回操作之中。 虛擬機(jī)可以從方法常量池中的方法表結(jié)構(gòu)中的ACC_SYNCHRONIZED訪問標(biāo)志得知一個(gè)方法是否被聲明為同步方法。 當(dāng)方法調(diào)用時(shí), 調(diào)用指令將會(huì)檢查方法的ACC_SYNCHRONIZED訪問標(biāo)志是否被設(shè)置, 如果設(shè)置了, 執(zhí)行線程就要求先成功持有管程, 然后才能執(zhí)行方法, 最后當(dāng)方法完成(無論是正常完成還是非正常完成) 時(shí)釋放管程。 在方法執(zhí)行期間, 執(zhí)行線程持有了管程, 其他任何線程都無法再獲取到同一個(gè)管程。 如果一個(gè)同步方法執(zhí)行期間拋出了異常, 并且在方法內(nèi)部無法處理此異常, 那這個(gè)同步方法所持有的管程將在異常拋到同步方法邊界之外時(shí)自動(dòng)釋放。同步一段指令集序列通常是由Java語言中的synchronized語句塊來表示的, Java虛擬機(jī)的指令集中monitorenter和monitorexit兩條指令來支持synchronized關(guān)鍵字的語義, 正確實(shí)現(xiàn)synchronized關(guān)鍵字需要Javac編譯器與Java虛擬機(jī)兩者共同協(xié)作支持 .
下篇文章針對(duì)這點(diǎn)做實(shí)例分析。
總結(jié)
以上是生活随笔為你收集整理的JVM004_字节码指令简介的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring Boot————Web应用
- 下一篇: 多线程相关知识