天天看點

深入了解JVM - 虛拟機位元組碼指令集

Java虛拟機的指令由一個位元組長度的、代表着某種特定操作含義的數字(稱為操作碼,Opcode)以及跟随其後的零至多個代表此操作所需的參數(稱為操作數,Operand)構成。因為隻有一個位元組的長度,是以指令總數不能超過256個。

在Java虛拟機的指令集中,大多數指令都包含其操作所對應的資料類型資訊,如:i代表對int類型的資料操作,l代表long,s代表short,b代表byte,c代表char,f代表float,d代表double,a代表reference。

解釋器的執行模型

Java虛拟機的解釋器的執行模型:

do {
    自動計算PC寄存器的值加1;
    根據PC寄存器訓示的位置,從位元組碼流中取出操作碼;
    if (位元組碼存在操作數) 從位元組碼流中取出操作數;
    執行操作碼所定義的操作;
} while (位元組碼流長度 > 0);      

常量入棧指令

指令碼 操作碼(助記符) 操作數 描述(棧指操作數棧)
0x01 aconst_null 将 null推送至棧頂
0x02 iconst_m1 将 -1(int)推送至棧頂
0x03 iconst_0 将 0(int)推送至棧頂
0x04 iconst_1 将 1(int)推送至棧頂
0x05 iconst_2 将 2(int)推送至棧頂
0x06 iconst_3 将 3(int)推送至棧頂
0x07 iconst_4 将 4(int)推送至棧頂
0x08 iconst_5 将 5(int)推送至棧頂
0x09 lconst_0 将 0(long)推送至棧頂
0x0a lconst_1 将 1(long)推送至棧頂
0x0b fconst_0 将 0(float)推送至棧頂
0x0c fconst_1 将 1(float)推送至棧頂
0x0d fconst_2 将 2(float)推送至棧頂
0x0e dconst_0 将 0(double)推送至棧頂
0x0f dconst_1 将 1(double)推送至棧頂
0x10 bipush valuebyte 将一個byte值帶符号擴充成int推送至棧頂
0x11 sipush

valuebyte1

valuebyte2

将一個short值帶符号擴充成int推送至棧頂
0x12 ldc indexbyte1 将int、float或String型常量值從常量池中推送至棧頂
0x13 ldc_w

indexbyte1

indexbyte2

将int、float或String型常量值從常量池中推送至棧頂(寬索引)
0x14 ldc2_w

indexbyte1

indexbyte2

将long或double型常量值從常量池中推送至棧頂(寬索引)

局部變量值轉載到棧中指令

指令碼 操作碼(助記符) 操作數 描述(棧指操作數棧)
0x15 iload indexbyte 從局部變量indexbyte中裝載int類型值推送至棧頂
0x16 lload indexbyte 從局部變量indexbyte中裝載long類型值推送至棧頂
0x17 fload indexbyte 從局部變量indexbyte中裝載float類型值推送至棧頂
0x18 dload indexbyte 從局部變量indexbyte中裝載double類型值推送至棧頂
0x19 aload indexbyte 從局部變量indexbyte中裝載引用類型值推送至棧頂
0x1a iload_0 将第1個int型本地變量推送至棧頂
0x1b iload_1 将第2個int型本地變量推送至棧頂
0x1c iload_2 将第4個int型本地變量推送至棧頂
0x1d iload_3 将第4個int型本地變量推送至棧頂
0x1e lload_0 将第1個long型本地變量推送至棧頂
0x1f lload_1 将第2個long型本地變量推送至棧頂
0x20 lload_2 将第3個long型本地變量推送至棧頂
0x21 lload_3 将第4個long型本地變量推送至棧頂
0x22 fload_0 将第1個float型本地變量推送至棧頂
0x23 fload_1 将第2個float型本地變量推送至棧頂
0x24 fload_2 将第3個float型本地變量推送至棧頂
0x25 fload_3 将第4個float型本地變量推送至棧頂
0x26 dload_0 将第1個double型本地變量推送至棧頂
0x27 dload_1 将第2個double型本地變量推送至棧頂
0x28 dload_2 将第3個double型本地變量推送至棧頂
0x29 dload_3 将第4個double型本地變量推送至棧頂
0x2a aload_0 将第1個引用類型本地變量推送至棧頂
0x2b aload_1 将第2個引用類型本地變量推送至棧頂
0x2c aload_2 将第3個引用類型本地變量推送至棧頂
0x2d aload_3 将第4個引用類型本地變量推送至棧頂
0x2e iaload 将int類型數組的索引值推送至棧頂
0x2f laload 将long類型數組的索引值推送至棧頂
0x30 faload 将float類型數組的索引值推送至棧頂
0x31 daload 将double類型數組的索引值推送至棧頂
0x32 aaload 将引用類型數組的索引值推送至棧頂
0x33 baload 将boolean或byte類型數組的索引值推送至棧頂(先轉換為int類型值,後壓棧)
0x34 caload 将char類型數組的索引值推送至棧頂(先轉換為int類型值,後壓棧)
0x35 saload 将short類型數組的索引值推送至棧頂(先轉換為int類型值,後壓棧)

将棧頂值儲存到局部變量中指令

指令碼 操作碼(助記符) 操作數 描述(棧指操作數棧)
0x36 (wide)istore indexbyte 将棧頂int類型值儲存到局部變量indexbyte中
0x37 (wide)lstore indexbyte 将棧頂long類型值儲存到局部變量indexbyte中
0x38 (wide)fstore indexbyte 将棧頂float類型值儲存到局部變量indexbyte中
0x39 (wide)dstore indexbyte 将棧頂double類型值儲存到局部變量indexbyte中
0x3a (wide)astore indexbyte 将棧頂引用類型值儲存到局部變量indexbyte中
0x3b istore_0 将棧頂int類型值儲存到局部變量0中
0x3c istore_1 将棧頂int類型值儲存到局部變量1中
0x3d istore_2 将棧頂int類型值儲存到局部變量2中
0x3e istore_3 将棧頂int類型值儲存到局部變量3中
0x3f lstore_0 将棧頂long類型值儲存到局部變量0中
0x40 lstore_1 将棧頂long類型值儲存到局部變量1中
0x41 lstore_2 将棧頂long類型值儲存到局部變量2中
0x42 lstroe_3 将棧頂long類型值儲存到局部變量3中
0x43 fstore_0 将棧頂float類型值儲存到局部變量0中
0x44 fstore_1 将棧頂float類型值儲存到局部變量1中
0x45 fstore_2 将棧頂float類型值儲存到局部變量2中
0x46 fstore_3 将棧頂float類型值儲存到局部變量3中
0x47 dstore_0 将棧頂double類型值儲存到局部變量0中
0x48 dstore_1 将棧頂double類型值儲存到局部變量1中
0x49 dstore_2 将棧頂double類型值儲存到局部變量2中
0x4a dstore_3 将棧頂double類型值儲存到局部變量3中
0x4b astroe_0 将棧頂引用類型值儲存到局部變量0中
0x4c astore_1 将棧頂引用類型值儲存到局部變量1中
0x4d astore_2 将棧頂引用類型值儲存到局部變量2中
0x4e astore_3 将棧頂引用類型值儲存到局部變量3中
0x4f iastore 将棧頂int類型值儲存到指定int類型數組的指定索引位
0x50 lastore 将棧頂long類型值儲存到指定long類型數組的指定索引位
0x51 fastore 将棧頂float類型值儲存到指定float類型數組的指定索引位
0x52 dastore 将棧頂double類型值儲存到指定double類型數組的指定索引位
0x53 aastore 将棧頂引用類型值儲存到指定引用類型數組的指定索引位
0x54 bastroe 将棧頂boolean類型值或byte類型值儲存到指定boolean類型數組或byte類型數組的指定索引位
0x55 castore 将棧頂char類型值儲存到指定char類型數組的指定索引位
0x56 sastore 将棧頂short類型值儲存到指定short類型數組的指定索引位

通用(無類型)棧操作指令

指令碼 操作碼(助記符) 操作數 描述(棧指操作數棧)
0x00 nop 空操作
0x57 pop 從棧頂彈出一個字長的資料(不是long和double)
0x58 pop2 從棧頂彈出兩個字長的資料(一個long或double,或者是兩個單位元組長度數值)
0x59 dup 複制棧頂一個字長的資料,将複制後的資料壓入棧頂
0x5a dup_x1 複制棧頂一個字長的資料,彈出棧頂兩個字長資料,先将複制後的資料壓入棧頂,再将彈出的兩個字長資料壓入棧頂
0x5b dup_x2 複制棧頂一個字長的資料,彈出棧頂三個字長的資料,将複制後的資料壓入棧頂,再将彈出的三個字長的資料壓入棧頂
0x5c dup2 複制棧頂兩個字長的資料,将複制後的兩個字長的資料壓入棧頂
0x5d dup2_x1 dup_x1 指令的雙倍版
0x5e dup2_x2 dup_x2 指令的雙倍版
0x5f swap 将棧最頂端的兩個數值互換(數值不能是long或double)

整數和浮動點數運算

指令碼 操作碼(助記符) 操作數 描述(棧指操作數棧)
0x60 iadd 将棧頂兩int類型數相加,并将結果壓入棧頂
0x61 ladd 将棧頂兩long類型數相加,并将結果壓入棧頂
0x62 fadd 将棧頂兩float類型數相加,并将結果壓入棧頂
0x63 dadd 将棧頂兩double類型數相加,并将結果壓入棧頂
0x64 isub 将棧頂兩int類型數相減,并将結果壓入棧頂
0x65 lsub 将棧頂兩long類型數相減,并将結果壓入棧頂
0x66 fsub 将棧頂兩float類型數相減,并将結果壓入棧頂
0x67 dsub 将棧頂兩double類型數相減,并将結果壓入棧頂
0x68 imul 将棧頂兩int類型數相乘,并将結果壓入棧頂
0x69 lmul 将棧頂兩long類型數相乘,并将結果壓入棧頂
0x6a fmul 将棧頂兩float類型數相乘,并将結果壓入棧頂
0x6b dmul 将棧頂兩double類型數相乘,并将結果壓入棧頂
0x6c idiv 将棧頂兩int類型數相除,并将結果壓入棧頂
0x6d ldiv 将棧頂兩long類型數相除,并将結果壓入棧頂
0x6e fdiv 将棧頂兩float類型數相除,并将結果壓入棧頂
0x6f ddiv 将棧頂兩double類型數相除,并将結果壓入棧頂
0x70 irem 将棧頂兩int類型數取模,并将結果壓入棧頂
0x71 lrem 将棧頂兩long類型數取模,并将結果壓入棧頂
0x72 frem 将棧頂兩float類型數取模,并将結果壓入棧頂
0x73 drem 将棧頂兩double類型數取模,并将結果壓入棧頂
0x74 ineg 将棧頂int類型值取負,并将結果壓入棧頂
0x75 lneg 将棧頂long類型值取負,并将結果壓入棧頂
0x76 fneg 将棧頂float類型值取反,并将結果壓入棧頂
0x77 dneg 将棧頂double類型值取負,并将結果壓入棧頂
0x84 (wide)iinc

indexbyte

constbyte

将整數值constbyte加到indexbyte指定的int類型的局部變量中(i++,i–,i+=2;)

邏輯運算 - 移位運算

指令碼 操作碼(助記符) 操作數棧 描述(棧指操作數棧)
0x78 ishl … , a , n (a << n) 左移int類型值,并将結果壓入棧頂
0x79 lshl … , a , n (a << n) 左移long類型值,并将結果壓入棧頂
0x7a ishr … , a , n (a >> n) 算術右移int類型值,并将結果壓入棧頂
0x7b lshr … , a , n (a >> n) 算術右移long類型值,并将結果壓入棧頂
0x7c iushr … , a , n (a >>> n) 邏輯右移int類型值,并将結果壓入棧頂
0x7d lushr … , a , n (a >>> n) 邏輯右移long類型值,并将結果壓入棧頂

邏輯運算 - 位運算

指令碼 操作碼(助記符) 操作數棧 描述(棧指操作數棧)
0x7e iand … , a , n (a & b) 對int類型按位與運算,并将結果壓入棧頂
0x7f land … , a , n (a & b) 對long類型的按位與運算,并将結果壓入棧頂
0x80 ior … , a , n (a | b) 對int類型的按位或運算,并将結果壓入棧頂
0x81 lor … , a , n (a | b) 對long類型的按位或運算,并将結果壓入棧頂
0x82 ixor … , a , n (a ^ b) 對int類型的按位異或運算,并将結果壓入棧頂
0x83 lxor … , a , n (a ^ b) 對long類型的按位異或運算,并将結果壓入棧頂

類型轉換指令

指令碼 操作碼(助記符) 操作數棧 描述(棧指操作數棧)
0x85 i2l … , a (long) a, 将棧頂int類型值轉換為long類型值,并将結果壓入棧頂
0x86 i2f … , a (float) a, 将棧頂int類型值轉換為float類型值,并将結果壓入棧頂
0x87 i2d … , a (double) a, 将棧頂int類型值轉換為double類型值,并将結果壓入棧頂
0x88 l2i … , a (int) a, 将棧頂long類型值轉換為int類型值,并将結果壓入棧頂
0x89 l2f … , a (float) a, 将棧頂long類型值轉換為float類型值,并将結果壓入棧頂
0x8a l2d … , a (double) a, 将棧頂long類型值轉換double類型值,并将結果壓入棧頂
0x8b f2i … , a (int) a, 将棧頂float類型值轉換為int類型值,并将結果壓入棧頂
0x8c f2l … , a (long) a, 将棧頂float類型值轉換為long類型值,并将結果壓入棧頂
0x8d f2d … , a (double) a, 将棧頂float類型值轉換為double類型值,并将結果壓入棧頂
0x8e d2i … , i (double) a, 将棧頂double類型值轉換為int類型值,并将結果壓入棧頂
0x8f d2l … , a (long) a, 将棧頂double類型值轉換為long類型值,并将結果壓入棧頂
0x90 d2f … , a (float) a, 将棧頂double類型值轉換為float類型值,并将結果壓入棧頂
0x91 i2b … , a (byte) a, 将棧頂int類型值截斷成byte類型,後帶符号擴充成int類型值壓入棧頂
0x92 i2c … , a (char) a, 将棧頂int類型值截斷成char類型值,後帶符号擴充成int類型值壓入棧頂
0x93 i2s … , a (short) a, 将棧頂int類型值截斷成short類型值,後帶符号擴充成int類型值壓入棧頂

控制流指令 - 比較指令

指令碼 操作碼(助記符) 操作數棧 描述(棧指操作數棧)
0x94 lcmp … , a , b 比較棧頂兩long類型值,前者大,1入棧;相等,0入棧;後者大,-1入棧 [a == b ? 0 : (a < b ? -1 : 1)]
0x95 fcmpl … , a , b 比較棧頂兩float類型值,前者大,1入棧;相等,0入棧;後者大,-1入棧;有NaN存在,-1入棧 [a == b ? 0 : (a < b ? -1 : 1)]
0x96 fcmpg … , a , b 比較棧頂兩float類型值,前者大,1入棧;相等,0入棧;後者大,-1入棧;有NaN存在,-1入棧 [a == b ? 0 : (a < b ? -1 : 1)]
0x97 dcmpl … , a , b 比較棧頂兩double類型值,前者大,1入棧;相等,0入棧;後者大,-1入棧;有NaN存在,-1入棧 [a == b ? 0 : (a < b ? -1 : 1)]
0x98 dcmpg … , a , b 比較棧頂兩double類型值,前者大,1入棧;相等,0入棧;後者大,-1入棧;有NaN存在,-1入棧 [a == b ? 0 : (a < b ? -1 : 1)]

控制流指令 - 條件跳轉指令

指令碼 操作碼(助記符) 棧操作之前 棧操作之後 描述(棧指操作數棧)
0x99 ifeq … , i 若棧頂int類型值為0則跳轉 (jump if i == 0)
0x9a ifne … , i 若棧頂int類型值不為0則跳轉 (jump if i != 0)
0x9b iflt … , i 若棧頂int類型值小于0則跳轉 (jump if i < 0)
0x9c ifge … , i 若棧頂int類型值大于等于0則跳轉 (jump if i >= 0)
0x9d ifgt … , i 若棧頂int類型值大于0則跳轉 (jump if i > 0)
0x9e ifle … , i 若棧頂int類型值小于等于0則跳轉 (jump if i <= 0)
0x9f if_icmpeq … , i , j 若棧頂兩int類型值相等則跳轉 (jump if i == j)
0xa0 if_icmpne … , i , j 若棧頂兩int類型值不相等則跳轉 (jump if i != j)
0xa1 if_icmplt … , i , j 若棧頂兩int類型值前小于後則跳轉 (jump if i < j)
0xa4 if_icmple … , i , j 若棧頂兩int類型值前小于等于後則跳轉 (jump if i <= j)
0xa3 if_icmpgt … , i , j 若棧頂兩int類型值前大于後則跳轉 (jump if i > j)
0xa2 if_icmpge … , i , j 若棧頂兩int類型值前大于等于後則跳轉 (jump if i >= j)
0xa5 if_acmpeq … , o , p 若棧頂兩引用類型值相等則跳轉 (jump if o == p)
0xa6 if_acmpne … , o , p 若棧頂兩引用類型值不相等則跳轉 (jump if o != p)
0xc6 ifnull … , o 若棧頂引用值為null則跳轉 (jump if o == null)
0xc7 ifnonnull … , o 若棧頂引用值不為null則跳轉 (jump if o != null)

控制流指令 - 無條件跳轉指令

指令碼 操作碼(助記符) 操作數 描述(棧指操作數棧)
0xa7 goto

branchbyte1

branchbyte2

無條件跳轉到指定位置
0xc8 goto_w

branchbyte1

branchbyte2

branchbyte3

branchbyte4

無條件跳轉到指定位置(寬索引)

控制流指令 - 表跳轉指令

指令碼 操作碼(助記符) 操作數 描述(棧指操作數棧)
0xaa tableswitch

<0 - 3bytepad>

defaultbyte1

defaultbyte2

defaultbyte3

defaultbyte4

lowbyte1

lowbyte2

lowbyte3

lowbyte4

highbyte1

highbyte2

highbyte3

highbyte4

jump offsets…

通過索引通路跳轉表,并跳轉

jump always

0xab lookupswitch

<0 - 3bytepad>

defaultbyte1

defaultbyte2

defaultbyte3

defaultbyte4

npairs1

npairs2

npairs3

npairs4

match offsets

通過鍵值通路跳轉表,并跳轉

jump always

控制流指令 - 異常和finally

指令碼 操作碼(助記符) 操作數 描述(棧指操作數棧)
0xbf athrow 抛出異常
0xa8 jsr

branchbyte1

branchbyte2

跳轉到子例程式
0xc9 jsr_w

branchbyte1

branchbyte2

branchbyte3

branchbyte4

跳轉到子例程式(寬索引)
0xa9 (wide)ret indexbyte 傳回子例程式

對象操作指令

指令碼 操作碼(助記符) 操作數 描述(棧指操作數棧)
0xbb new

indexbyte1

indexbyte2

建立新的對象執行個體,并将其引用壓入棧頂
0xc0 checkcast

indexbyte1

indexbyte

類型強轉,如果該檢查未通過将會抛出ClassCastException異常
0xc1 instanceof

indexbyte1

indexbyte2

檢查對象是否是指定的類的執行個體。如果是,1進棧;否則,0進棧
0xb2 getstatic

indexbyte1

indexbyte2

擷取靜态字段的值,并将其引用壓入棧頂
0xb3 putstatic

indexbyte1

indexbyte2

給靜态字段指派
0xb4 getfield

indexbyte1

indexbyte2

擷取對象字段的值,并将其引用壓入棧頂
0xb5 putfield

indexbyte1

indexbyte2

給對象字段指派

數組操作指令

指令碼 操作碼(助記符) 操作數 描述(棧指操作數棧)
0xbc newarray atype 建立type類型的數組
0xbd anewarray

indexbyte1

indexbyte2

建立引用類型的數組
0xbe arraylength 擷取一維數組的長度
0xc5 multianewarray

indexbyte1

indexbyte2

dimension

建立dimension次元的數組

方法調用指令

指令碼 操作碼(助記符) 操作數 描述(棧指操作數棧)
0xb7 invokespecial

indexbyte1

indexbyte2

編譯時方法綁定調用方法
0xb6 invokevirtual

indexbyte1

indexbyte2

運作時方法綁定調用方法
0xb8 invokestatic

indexbyte1

indexbyte2

調用靜态方法
0xb9 invokeinterface

indexbyte1

indexbyte2

count

調用接口方法

方法傳回指令

指令碼 操作碼(助記符) 描述(棧指操作數棧)
0xac ireturn 傳回int類型值
0xad lreturn 傳回long類型值
0xae freturn 傳回float類型值
0xaf dreturn 傳回double類型值
0xb0 areturn 傳回引用類型值
0xb1 return void函數傳回

線程同步指令

指令碼 操作碼(助記符) 描述(棧指操作數棧)
0xc2 monitorenter 進入并獲得對象螢幕,擷取對象鎖,用于同步方法或者同步塊
0xc3 monitorexit 釋放并退出對象螢幕,釋放對象鎖,用于同步方法或者同步塊

wide指令

指令碼 操作碼(助記符) 描述(棧指操作數棧)
0xc4 wide 使用附加位元組擴充局部變量的寬度(iinc指令特殊)

示例

方法的執行示例

Java源碼:

public int inc() {
    int x;
    try {
        x = 1;
        return x;
    } catch (Exception e) {
        x = 2;
        return x;
    } finally {
        x = 3;
    }
}      

編譯後的ByteCode位元組碼及異常表:

public int inc();
    Code:
        Stack=1, Locals=5, Args_size=1
        0:   iconst_1   // try塊中的x=1
        1:   istore_1
        2:   iload_1    // 儲存x到returnValue中,此時x=1
        3:   istore  4
        5:   iconst_3   // finaly塊中的x=3
        6:   istore_1
        7:   iload   4  // 将returnValue中的值放到棧頂,準備給ireturn傳回
        9:   ireturn
        10:  astore_2   // 給catch中定義的Exception e指派,存儲在變量槽 2中
        11:  iconst_2   // catch塊中的x=2
        12:  istore_1
        13:  iload_1    // 儲存x到returnValue中,此時x=2
        14:  istore  4
        16:  iconst_3   // finaly塊中的x=3
        17:  istore_1
        18:  iload 4    // 将returnValue中的值放到棧頂,準備給ireturn傳回
        20:  ireturn
        21:  astore_3   // 如果出現了不屬于java.lang.Exception及其子類的異常才會走到這裡
        22:  iconst_3   // finaly塊中的x=3
        23:  istore_1
        24:  aload_3    // 将異常放置到棧頂,并抛出
        25:  athrow
    Exception table:
    from   to  target type
        0     5    10   Class java/lang/Exception
        0     5    21   any
        10    16   21   any      

編譯器為這段Java源碼生成了三條異常表記錄,對應三條可能出現的代碼執行路徑。從Java代碼的語義上講,這三條執行路徑分别為:

  • 如果try語句塊中出現屬于Exception或其子類的異常,轉到catch語句塊處理;
  • 如果try語句塊中出現不屬于Exception或其子類的異常,轉到finally語句塊處理;
  • 如果catch語句塊中出現任何異常,轉到finally語句塊處理。

參考

繼續閱讀