jvm理论-字节码指令
Java虛擬機的指令由一個字節長度的、代表著某種特定操作含義的數字(稱為操作碼,Opcode)以及跟隨其后的零至多個代表此操作所需參數(稱為操作數,Operands)而構成。
基本數據類型
1、除了long和double類型外,每個變量都占局部變量區中的一個變量槽(slot),而long及double會占用兩個連續的變量槽。
2、大多數對于boolean、byte、short和char類型數據的操作,都使用相應的int類型作為運算類型。
?
加載和存儲指令
1、將一個局部變量加載到操作棧:iload、iload_<n>、lload、lload_<n>、fload、fload_<n>、dload、dload_<n>、aload、aload_<n>。
2、將一個數值從操作數棧存儲到局部變量表:istore、istore_<n>、lstore、lstore_<n>、fstore、fstore_<n>、dstore、dstore_<n>、astore、astore_<n>。
3、將一個常量加載到操作數棧:bipush、sipush、ldc、ldc_w、ldc2_w、aconst_null、iconst_m1、iconst_<i>、lconst_<l>、fconst_<f>、dconst_<d>。
4、擴充局部變量表的訪問索引的指令:wide。
_<n>:_0、_1、_2、_3,
存儲數據的操作數棧和局部變量表主要就是由加載和存儲指令進行操作,除此之外,還有少量指令,如訪問對象的字段或數組元素的指令也會向操作數棧傳輸數據。
二、const系列
該系列命令主要負責把簡單的數值類型送到棧頂。該系列命令不帶參數。注意只把簡單的數值類型送到棧頂時,才使用如下的命令。
比如對應int型才該方式只能把-1,0,1,2,3,4,5(分別采用iconst_m1,iconst_0, iconst_1, iconst_2, iconst_3, iconst_4, iconst_5)
送到棧頂。對于int型,其他的數值請使用push系列命令(比如bipush)。
指令碼 ? ?助記符 ? ? ? ? ? ? ? ? ? ? ? ? ? ?說明
0x02?? ? ? ??iconst_m1?? ? ? ? ? ? ? ? ? 將int型(-1)推送至棧頂
0x03?? ? ? ? iconst_0 ? ? ? ? ? ? ? ? ? ? ?將int型(0)推送至棧頂
0x04?? ? ? ? iconst_1 ? ? ? ? ? ? ? ? ? ? ?將int型(1)推送至棧頂
0x05?? ? ? ? iconst_2 ? ? ? ? ? ? ? ? ? ? ?將int型(2)推送至棧頂
0x06?? ? ? ? iconst_3 ? ? ? ? ? ? ? ? ? ? ?將int型(3)推送至棧頂
0x07?? ? ? ? iconst_4 ? ? ? ? ? ? ? ? ? ? ?將int型(4)推送至棧頂
0x08?? ? ? ? iconst_5 ? ? ? ? ? ? ? ? ? ? ?將int型(5)推送至棧頂
0x09?? ? ? ??lconst_0?? ? ? ? ? ? ? ? ? ? ?將long型(0)推送至棧頂
0x0a?? ? ? ? lconst_1 ? ? ? ? ? ? ? ? ? ? ?將long型(1)推送至棧頂
0x0b?? ? ? ??fconst_0?? ? ? ? ? ? ? ? ? ? ?將float型(0)推送至棧頂
0x0c?? ? ? ? fconst_1 ? ? ? ? ? ? ? ? ? ? ?將float型(1)推送至棧頂
0x0d?? ? ? ? fconst_2 ? ? ? ? ? ? ? ? ? ? ?將float型(2)推送至棧頂
0x0e?? ? ? ??dconst_0?? ? ? ? ? ? ? ? ? ? 將double型(0)推送至棧頂
0x0f?? ? ? ? ?dconst_1 ? ? ? ? ? ? ? ? ? ? 將double型(1)推送至棧頂
三、push系列
該系列命令負責把一個整形數字(長度比較小)送到到棧頂。該系列命令有一個參數,用于指定要送到棧頂的數字。
注意該系列命令只能操作一定范圍內的整形數值,超出該范圍的使用將使用ldc命令系列。
指令碼 ? ?助記符 ? ? ? ? ? ? ? ? ? ? ? ? ? ?說明
0x10?? ? ? ? ?bipush ? ?將單字節的常量值(-128~127)推送至棧頂
0x11?? ? ? ? ? sipush ? ?將一個短整型常量值(-32768~32767)推送至棧頂
四、ldc系列
該系列命令負責把數值常量或String常量值從常量池中推送至棧頂。該命令后面需要給一個表示常量在常量池中位置(編號)的參數,
哪些常量是放在常量池呢?比如:final static int id=32768;final static float double=6.5。
對于const系列命令和push系列命令操作范圍之外的數值類型常量,都放在常量池中.
另外,所有不是通過new創建的String都是放在常量池中的。
指令碼 ? ?助記符 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 說明
0x12?? ? ? ? ? ?ldc ? ? ? ? ? ? ? ? 將int, float或String型常量值從常量池中推送至棧頂
0x13?? ? ? ? ?ldc_w ? ? ? ? ? ? ? 將int, float或String型常量值從常量池中推送至棧頂(寬索引)
0x14?? ? ? ? ?ldc2_w ? ? ? ? ? ? 將long或double型常量值從常量池中推送至棧頂(寬索引)
五、load系列
5.1、load系列A
該系列命令負責把本地變量的送到棧頂。這里的本地變量不僅可以是數值類型,還可以是引用類型。
對于前四個本地變量可以采用iload_0,iload_1,iload_2,iload_3(它們分別表示第0,1,2,3個整形變量)這種不到參數的簡化命令形式。
對于第4以上的本地變量將使用iload命令這種形式,在它后面給一參數,以表示是對第幾個(從0開始)本類型的本地變量進行操作。
對本地變量所進行的編號,是對所有類型的本地變量進行的(并不按照類型分類)。
對于非靜態函數,第一變量是this,即其對于的操作是aload_0.
還有函數傳入參數也算本地變量,在進行編號時,它是先于函數體的本地變量的。
指令碼 ? ?助記符 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?說明
0x15?? ? ? ? ?iload ? ? ? ? ? ? ? ? ? ? ? ? ?將指定的int型本地變量推送至棧頂
0x16?? ? ? ? ?lload ? ? ? ? ? ? ? ? ? ? ? ? ?將指定的long型本地變量推送至棧頂
0x17? ? ? ? ? fload ? ? ? ? ? ? ? ? ? ? ? ? ?將指定的float型本地變量推送至棧頂
0x18? ? ? ? ? dload ? ? ? ? ? ? ? ? ? ? ? ? 將指定的double型本地變量推送至棧頂
0x19? ? ? ? ? aload ? ? ? ? ? ? ? ? ? ? ? ? 將指定的引用類型本地變量推送至棧頂
0x1a? ? ? ? ? iload_0 ? ? ? ? ? ? ? ? ? ? ?將第一個int型本地變量推送至棧頂
0x1b? ? ? ? ? iload_1 ? ? ? ? ? ? ? ? ? ? ?將第二個int型本地變量推送至棧頂
0x1c? ? ? ? ? iload_2 ? ? ? ? ? ? ? ? ? ? ?將第三個int型本地變量推送至棧頂
0x1d? ? ? ? ? iload_3 ? ? ? ? ? ? ? ? ? ? ?將第四個int型本地變量推送至棧頂
0x1e? ? ? ? ? lload_0 ? ? ? ? ? ? ? ? ? ? ?將第一個long型本地變量推送至棧頂
0x1f? ? ? ? ? ?lload_1 ? ? ? ? ? ? ? ? ? ? ?將第二個long型本地變量推送至棧頂
0x20? ? ? ? ? lload_2 ? ? ? ? ? ? ? ? ? ? ?將第三個long型本地變量推送至棧頂
0x21? ? ? ? ? lload_3 ? ? ? ? ? ? ? ? ? ? ?將第四個long型本地變量推送至棧頂
0x22? ? ? ? ? fload_0 ? ? ? ? ? ? ? ? ? ? 將第一個float型本地變量推送至棧頂
0x23? ? ? ? ? fload_1 ? ? ? ? ? ? ? ? ? ? 將第二個float型本地變量推送至棧頂
0x24? ? ? ? ? fload_2 ? ? ? ? ? ? ? ? ? ? 將第三個float型本地變量推送至棧頂
0x25? ? ? ? ? fload_3 ? ? ? ? ? ? ? ? ? ? 將第四個float型本地變量推送至棧頂
0x26? ? ? ? ?dload_0 ? ? ? ? ? ? ? ? ? ? 將第一個double型本地變量推送至棧頂
0x27? ? ? ? ?dload_1 ? ? ? ? ? ? ? ? ? ? 將第二個double型本地變量推送至棧頂
0x28? ? ? ? ?dload_2 ? ? ? ? ? ? ? ? ? ? 將第三個double型本地變量推送至棧頂
0x29? ? ? ? ?dload_3 ? ? ? ? ? ? ? ? ? ? 將第四個double型本地變量推送至棧頂
0x2a? ? ? ? ?aload_0 ? ? ? ? ? ? ? ? ? ? 將第一個引用類型本地變量推送至棧頂
0x2b? ? ? ? ?aload_1 ? ? ? ? ? ? ? ? ? ? 將第二個引用類型本地變量推送至棧頂
0x2c? ? ? ? ?aload_2 ? ? ? ? ? ? ? ? ? ? 將第三個引用類型本地變量推送至棧頂
0x2d? ? ? ? ?aload_3 ? ? ? ? ? ? ? ? ? ? 將第四個引用類型本地變量推送至棧頂
5.2、load系列B
該系列命令負責把數組的某項送到棧頂。該命令根據棧里內容來確定對哪個數組的哪項進行操作。
比如,如果有成員變量:final String names[]={"robin","hb"};
那么這句話:String str=names[0];對應的指令為
? ?17: aload_0 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//將this引用推送至棧頂,即壓入棧。
? ?18: getfield #5; //Field names:[Ljava/lang/String;//將棧頂的指定的對象的第5個實例域(Field)的值(這個值可能是引用,這里就是引用)壓入棧頂
? ?21: iconst_0 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//數組的索引值(下標)推至棧頂,即壓入棧
? ?22: aaload ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//根據棧里內容來把name數組的第一項的值推至棧頂
? ?23: astore 5 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //把棧頂的值存到str變量里。因為str在我的程序中是其所在非靜態函數的第5個變量(從0開始計數),
指令碼 ? ?助記符 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 說明
0x2e?? ? ? ? iaload ? ? ? ? ? ? ? ? ? ? 將int型數組指定索引的值推送至棧頂
0x2f?? ? ? ? ?laload ? ? ? ? ? ? ? ? ? ? 將long型數組指定索引的值推送至棧頂
0x30?? ? ? ? faload ? ? ? ? ? ? ? ? ? ? 將float型數組指定索引的值推送至棧頂
0x31?? ? ? ?daload ? ? ? ? ? ? ? ? ? ? 將double型數組指定索引的值推送至棧頂
0x32?? ? ? ?aaload ? ? ? ? ? ? ? ? ? ? 將引用型數組指定索引的值推送至棧頂
0x33?? ? ? ?baload ? ? ? ? ? ? ? ? ? ? 將boolean或byte型數組指定索引的值推送至棧頂
0x34?? ? ? ?caload ? ? ? ? ? ? ? ? ? ? 將char型數組指定索引的值推送至棧頂
0x35?? ? ? ?saload ? ? ? ? ? ? ? ? ? ? 將short型數組指定索引的值推送至棧頂
六、store系列
6.1、store系列A
該系列命令負責把棧頂的值存入本地變量。這里的本地變量不僅可以是數值類型,還可以是引用類型。
如果是把棧頂的值存入到前四個本地變量的話,采用的是istore_0,istore_1,istore_2,istore_3(它們分別表示第0,1,2,3個本地整形變量)這種不到參數的簡化命令形式。如果是把棧頂的值存入到第四個以上本地變量的話,將使用istore命令這種形式,在它后面給一參數,以表示是把棧頂的值存入到第幾個(從0開始)本地變量中。
對本地變量所進行的編號,是對所有類型的本地變量進行的(并不按照類型分類)。
對于非靜態函數,第一變量是this,它是只讀的.
還有函數傳入參數也算本地變量,在進行編號時,它是先于函數體的本地變量的。
指令碼 ? ?助記符 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 說明
0x36?? ? ? ? istore ? ? ? ? ? ? ? ? ? ?將棧頂int型數值存入指定本地變量
0x37?? ? ? ? lstore ? ? ? ? ? ? ? ? ? ?將棧頂long型數值存入指定本地變量
0x38?? ? ? ? fstore ? ? ? ? ? ? ? ? ? ?將棧頂float型數值存入指定本地變量
0x39?? ? ? ? dstore ? ? ? ? ? ? ? ? ? 將棧頂double型數值存入指定本地變量
0x3a?? ? ? ? astore ? ? ? ? ? ? ? ? ? 將棧頂引用型數值存入指定本地變量
0x3b?? ? ? ? istore_0 ? ? ? ? ? ? ? ?將棧頂int型數值存入第一個本地變量
0x3c?? ? ? ? istore_1 ? ? ? ? ? ? ? ?將棧頂int型數值存入第二個本地變量
0x3d?? ? ? ? istore_2 ? ? ? ? ? ? ? ?將棧頂int型數值存入第三個本地變量
0x3e?? ? ? ? istore_3 ? ? ? ? ? ? ? ?將棧頂int型數值存入第四個本地變量
0x3f?? ? ? ? ?lstore_0 ? ? ? ? ? ? ? ?將棧頂long型數值存入第一個本地變量
0x40?? ? ? ? lstore_1 ? ? ? ? ? ? ? ?將棧頂long型數值存入第二個本地變量
0x41?? ? ? ? lstore_2 ? ? ? ? ? ? ? ?將棧頂long型數值存入第三個本地變量
0x42?? ? ? ? lstore_3 ? ? ? ? ? ? ? ?將棧頂long型數值存入第四個本地變量
0x43?? ? ? ? fstore_0 ? ? ? ? ? ? ? ?將棧頂float型數值存入第一個本地變量
0x44?? ? ? ? fstore_1 ? ? ? ? ? ? ? ?將棧頂float型數值存入第二個本地變量
0x45?? ? ? ? fstore_2 ? ? ? ? ? ? ? ?將棧頂float型數值存入第三個本地變量
0x46?? ? ? ? fstore_3 ? ? ? ? ? ? ? ?將棧頂float型數值存入第四個本地變量
0x47?? ? ? ? dstore_0 ? ? ? ? ? ? ? 將棧頂double型數值存入第一個本地變量
0x48?? ? ? ? dstore_1 ? ? ? ? ? ? ? 將棧頂double型數值存入第二個本地變量
0x49?? ? ? ? dstore_2 ? ? ? ? ? ? ? 將棧頂double型數值存入第三個本地變量
0x4a?? ? ? ? dstore_3 ? ? ? ? ? ? ? 將棧頂double型數值存入第四個本地變量
0x4b?? ? ? ? astore_0 ? ? ? ? ? ? ? 將棧頂引用型數值存入第一個本地變量
0x4c?? ? ? ? astore_1 ? ? ? ? ? ? ? 將棧頂引用型數值存入第二個本地變量
0x4d?? ? ? ?astore_2 ? ? ? ? ? ? ? ?將棧頂引用型數值存入第三個本地變量
0x4e?? ? ? ?astore_3 ? ? ? ? ? ? ? ?將棧頂引用型數值存入第四個本地變量
6.2、store系列B
該系列命令負責把棧頂項的值存到數組里。該命令根據棧里內容來確定對哪個數組的哪項進行操作。
比如,如下代碼:
int moneys[]=new int[5];
moneys[1]=100;
其對應的指令為:
? ?49: iconst_5
? ?50: newarray int
? ?52: astore 11
???54: aload 11
? ?56: iconst_1
? ?57: bipush 100
? ?59: iastore
? ?60: lload 6 ? ? ? //因為str在我的程序中是其所非靜態在函數的第6個變量(從0開始計數).
指令碼 ? ?助記符 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 說明
0x4f?? ? ? ? iastore ? ? ? ? ? ? ? 將棧頂int型數值存入指定數組的指定索引位置
0x50?? ? ? ?lastore ? ? ? ? ? ? ? 將棧頂long型數值存入指定數組的指定索引位置
0x51?? ? ? ?fastore ? ? ? ? ? ? ? 將棧頂float型數值存入指定數組的指定索引位置
0x52?? ? ? ?dastore ? ? ? ? ? ? ?將棧頂double型數值存入指定數組的指定索引位置
0x53?? ? ? ?aastore ? ? ? ? ? ? ?將棧頂引用型數值存入指定數組的指定索引位置
0x54?? ? ? ?bastore ? ? ? ? ? ? ?將棧頂boolean或byte型數值存入指定數組的指定索引位置
0x55?? ? ? ?castore ? ? ? ? ? ? ?將棧頂char型數值存入指定數組的指定索引位置
0x56?? ? ? ?sastore ? ? ? ? ? ? ?將棧頂short型數值存入指定數組的指定索引位置
七、pop系列
該系列命令似乎只是簡單對棧頂進行操作,更多詳情待補充。
指令碼 ? ? 助記符 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 說明
0x57?? ? ? ? ? ?pop ? ? ? ? ? 將棧頂數值彈出 (數值不能是long或double類型的)
0x58?? ? ? ? ? ?pop2 ? ? ? ? 將棧頂的一個(long或double類型的)或兩個數值彈出(其它)
0x59?? ? ? ? ? ?dup ? ? ? ? ? 復制棧頂數值(數值不能是long或double類型的)并將復制值壓入棧頂
0x5a?? ? ? ? ? ?dup_x1 ? ? 復制棧頂數值(數值不能是long或double類型的)并將兩個復制值壓入棧頂
0x5b?? ? ? ? ? ?dup_x2 ? ? 復制棧頂數值(數值不能是long或double類型的)并將三個(或兩個)復制值壓入棧頂
0x5c?? ? ? ? ? ?dup2 ? ? ? ? 復制棧頂一個(long或double類型的)或兩個(其它)數值并將復制值壓入棧頂
0x5d?? ? ? ? ? ?dup2_x1 ? ?復制棧頂數值(long或double類型的)并將兩個復制值壓入棧頂
0x5e?? ? ? ? ? ?dup2_x2 ? ??復制棧頂數值(long或double類型的)并將三個(或兩個)復制值壓入棧頂
八、棧頂元素數學操作及移位操作系列
該系列命令用于對棧頂元素行數學操作,和對數值進行移位操作。移位操作的操作數和要移位的數都是從棧里取得。
比如對于代碼:int k=100;k=k>>1;其對應的JVM指令為:
? ?60: bipush 100
? ?62: istore 12//因為k在我的程序中是其所在非靜態函數的第12個變量(從0開始計數).
? ?64: iload 12
? ?66: iconst_1
? ?67: ishr
? ?68: istore 12
指令碼 ? ? 助記符 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?說明
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型數值取負并將結果壓入棧頂
0x78?? ? ? ? ? ?ishl?? ? ? ? ? ? ? 將int型數值左移位指定位數并將結果壓入棧頂
0x79?? ? ? ? ? ?lshl?? ? ? ? ? ? ? 將long型數值左移位指定位數并將結果壓入棧頂
0x7a?? ? ? ? ? ?ishr ? ?? ? ? ? ? ?將int型數值右(符號)移位指定位數并將結果壓入棧頂
0x7b?? ? ? ? ? ?lshr ? ? ? ? ? ? ??將long型數值右(符號)移位指定位數并將結果壓入棧頂
0x7c?? ? ? ? ? ?iushr ? ? ? ? ? ??將int型數值右(無符號)移位指定位數并將結果壓入棧頂
0x7d?? ? ? ? ? lushr ? ? ? ? ? ? ?將long型數值右(無符號)移位指定位數并將結果壓入棧頂
0x7e?? ? ? ? ? iand ? ? ? ? ? ? ? 將棧頂兩int型數值作“按位與”并將結果壓入棧頂
0x7f?? ? ? ? ? ?land ? ? ? ? ? ? ? 將棧頂兩long型數值作“按位與”并將結果壓入棧頂
0x80?? ? ? ? ? ?ior ? ? ? ? ? ? ? ? 將棧頂兩int型數值作“按位或”并將結果壓入棧頂
0x81?? ? ? ? ? ?lor ? ? ? ? ? ? ? ? 將棧頂兩long型數值作“按位或”并將結果壓入棧頂
0x82?? ? ? ? ? ?ixor ? ? ? ? ? ? ? 將棧頂兩int型數值作“按位異或”并將結果壓入棧頂
0x83?? ? ? ? ? ?lxor ? ? ? ? ? ? ? 將棧頂兩long型數值作“按位異或”并將結果壓入棧頂
?
?
運算指令
1、運算或算術指令用于對兩個操作數棧上的值進行某種特定運算,并把結果重新存入到操作棧頂。
2、算術指令分為兩種:整型運算的指令和浮點型運算的指令。
3、無論是哪種算術指令,都使用Java虛擬機的數據類型,由于沒有直接支持byte、short、char和boolean類型的算術指令,使用操作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。
類型轉換指令
1、類型轉換指令可以將兩種不同的數值類型進行相互轉換。
2、這些轉換操作一般用于實現用戶代碼中的顯式類型轉換操作,或者用來處理字節碼指令集中數據類型相關指令無法與數據類型一一對應的問題。
寬化類型轉換
int類型到long、float或者double類型。
long類型到float、double類型。
float類型到double類型。
i2l、f2b、l2f、l2d、f2d。
窄化類型轉換
i2b、i2c、i2s、l2i、f2i、f2l、d2i、d2l和d2f。
對象創建與訪問指令
創建類實例的指令:new。
創建數組的指令:newarray、anewarray、multianewarray。
訪問類字段(static字段,或者稱為類變量)和實例字段(非static字段,或者稱為實例變量)的指令:getfield、putfield、getstatic、putstatic。
把一個數組元素加載到操作數棧的指令:baload、caload、saload、iaload、laload、faload、daload、aaload。
將一個操作數棧的值存儲到數組元素中的指令:bastore、castore、sastore、iastore、fastore、dastore、aastore。
取數組長度的指令:arraylength。
檢查類實例類型的指令:instanceof、checkcast。
操作數棧管理指令
直接操作操作數棧的指令:
將操作數棧的棧頂一個或兩個元素出棧:pop、pop2。
復制棧頂一個或兩個數值并將復制值或雙份的復制值重新壓入棧頂:dup、dup2、dup_x1、dup2_x1、dup_x2、dup2_x2。
將棧最頂端的兩個數值互換:swap。
控制轉移指令
1、控制轉移指令可以讓Java虛擬機有條件或無條件地從指定的位置指令而不是控制轉移指令的下一條指令繼續執行程序。
2、從概念模型上理解,可以認為控制轉移指令就是在有條件或無條件地修改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。
復合條件分支:tableswitch、lookupswitch。
無條件分支:goto、goto_w、jsr、jsr_w、ret。
在Java虛擬機中有專門的指令集用來處理int和reference類型的條件分支比較操作,為了可以無須明顯標識一個實體值是否null,也有專門的指令用來檢測null值。
方法調用和返回指令
invokevirtual 指令用于調用對象的實例方法,根據對象的實際類型進行分派(虛方法分派),這也是Java語言中最常見的方法分派方式。
invokeinterface 指令用于調用接口方法,它會在運行時搜索一個實現了這個接口方法的對象,找出適合的方法進行調用。
invokespecial 指令用于調用一些需要特殊處理的實例方法,包括實例初始化(<init>)方法、私有方法和父類方法。
invokestatic ?調用靜態方法(static方法)。
invokedynamic 指令用于在運行時動態解析出調用點限定符所引用的方法,并執行該方法,前面4條調用指令的分派邏輯都固化在Java虛擬機內部,而invokedynamic指令的分派邏輯是由用戶所設定的引導方法決定的。
方法調用指令與數據類型無關,而方法返回指令是根據返回值的類型區分的,包括ireturn(當返回值是boolean、byte、char、short和int類型時使用)、lreturn、freturn、dreturn和areturn,另外還有一條return指令供聲明為void的方法、實例初始化方法以及類和接口的類初始化方法使用。
關于方法調用
1、Class文件的編譯過程中不包含傳統編譯中的連接步驟,所有方法調用中的目標方法在Class文件里面都是一個常量池中的符號引用,而不是方法在實際運行時內存布局中的入口地址。
2、在類加載的解析階段,會將其中的一部分符號引用轉化為直接引用,這類方法(編譯期可知,運行期不可變)的調用稱為解析(Resolution)。
主要包括靜態方法和私有方法兩大類,前者與類型直接關聯,后者在外部不可被訪問,這兩種方法各自的特點決定了它們都不可能通過繼承或別的方式重寫其他版本,因此它們都適合在類加載階段進行解析。
3、只要能被invokestatic和invokespecial指令調用的方法,都可以在解析階段中確定唯一的調用版本,符合這個條件的有靜態方法、私有方法、實例構造器、父類方法4類,它們在類加載的時候就會把符號引用解析為該方法的直接引用。
關于分派調用
1、靜態分派 - 方法重載
/*方法靜態分派演示*/public class StaticDispatch{static abstract class Human{}static class Man extends Human{}static class Woman extends Human{}public void sayHello(Human guy){System.out.println("hello,guy!");}public void sayHello(Man guy){System.out.println("hello,gentleman!");}public void sayHello(Woman guy){System.out.println("hello,lady!");}public static void main(String[]args){Human man=new Man();Human woman=new Woman();StaticDispatch sr=new StaticDispatch();sr.sayHello(man);sr.sayHello(woman);}}兩次輸出都是?hello,guy!
2、動態分派 - 方法重寫
public class DynamicDispatch{static abstract class Human{protected abstract void sayHello();}static class Man extends Human{@Overrideprotected void sayHello(){System.out.println("man say hello");}}static class Woman extends Human{@Overrideprotected void sayHello(){System.out.println("woman say hello");}}public static void main(String[]args){Human man=new Man();Human woman=new Woman();man.sayHello();woman.sayHello();man=new Woman();man.sayHello();} }man say hello
woman say hello
woman say hello
3、單分配、多分配
/** *單分派、多分派演示 */ public class Dispatch{static class QQ{}static class _360{}public static class Father{public void hardChoice(QQ arg){System.out.println("father choose qq");}public void hardChoice(_360 arg){System.out.println("father choose 360");}}public static class Son extends Father{public void hardChoice(QQ arg){System.out.println("son choose qq");}public void hardChoice(_360 arg){System.out.println("son choose 360");}}public static void main(String[]args){Father father=new Father();Father son=new Son();father.hardChoice(new _360());son.hardChoice(new QQ());} }father choose 360
son choose qq
4、動態語言支持
動態類型語言的關鍵特征是它的類型檢查的主體過程是在運行期而不是編譯期。
?
異常處理指令
在Java程序中顯式拋出異常的操作(throw語句)都由athrow指令來實現,除了用throw語句顯式拋出異常情況之外,Java虛擬機規范還規定了許多運行時異常會在其他Java虛擬機指令檢測到異常狀況時自動拋出。
例如,在整數運算中,當除數為零時,虛擬機會在idiv或ldiv指令中拋出ArithmeticException異常。
而在Java虛擬機中,處理異常(catch語句)不是由字節碼指令來實現的(很久之前曾經使用jsr和ret指令來實現,現在已經不用了),而是采用異常表來完成的。
同步指令
Java虛擬機可以支持方法級的同步和方法內部一段指令序列的同步,這兩種同步結構都是使用管程(Monitor)來支持的。
方法級同步
方法級的同步是隱式的,即無須通過字節碼指令來控制
它實現在方法調用和返回操作之中。虛擬機可以從方法常量池的方法表結構中的ACC_SYNCHRONIZED訪問標志得知一個方法是否聲明為同步方法。當方法調用時,調用指令將會檢查方法的ACC_SYNCHRONIZED訪問標志是否被設置,如果設置了,執行線程就要求先成功持有管程,然后才能執行方法,最后當方法完成(無論是正常完成還是非正常完成)時釋放管程。在方法執行期間,執行線程持有了管程,其他任何線程都無法再獲取到同一個管程。如果一個同步方法執行期間拋出了異常,并且在方法內部無法處理此異常,那么這個同步方法所持有的管程將在異常拋到同步方法之外時自動釋放。
方法內部一段指令序列的同步
同步一段指令集序列通常是由Java語言中的synchronized語句塊來表示的,Java虛擬機的指令集中有monitorenter和monitorexit兩條指令來支持synchronized關鍵字的語義,正確實現synchronized關鍵字需要Javac編譯器與Java虛擬機兩者共同協作支持。
?
參考:
http://ifeve.com/javacode2bytecode/
http://ifeve.com/javacode2bytecode2/
http://ifeve.com/java-code-to-byte-code-3/
總結
以上是生活随笔為你收集整理的jvm理论-字节码指令的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Redis Streams 介绍
- 下一篇: 意想不到的有趣linux命令