JVM的位元組碼指令

JVM的位元組碼指令

先來看看什麼是位元組碼指令

上圖的Code區域即為位元組碼指令。

JVM的指令是由一個位元組長度的、代表某種操作的數字(例如iconst_0,iconst_0前面的資料表示語句的順序,可用於條件判斷語句)以及跟在其後的0到多個運算元(或是運算元的索引)構成的(上圖指令沒有運算元),大多數指令都包含了其操作所對應的資料型別的資訊,例如iload指令將int型資料從區域性變數表載入到運算元棧中,由於JVM指令只有一個位元組,這意味著無法讓每種指令都支援所有資料型別,所以一些指令在必要的時候會將一些不支援的型別轉換成支援的型別,在將char、byte、short型資料放進區域性變數表或運算元棧時會轉換成int型資料,可能就是為了方便一些指令進行操作。

一般情況下指令中的首字母由以下含義:i—int,l—long,s—short,b—byte,c—char,f—float,d—double,a—reference(引用)

JVM的指令集大致分為以下幾種:

將一個資料從區域性變數表載入到運算元棧:

iload、lload、fload、dload、aload,其後接的是運算元的區域性變數表索引,可以帶wide進行擴充套件,例如iload的指令格式為iload index,帶wide:wide iload index1 index2,此時運算元下標為(index<<8)| index8

iload_n、lload_n、fload_n、dload_n、aload_n(n=0、1、2、3),代表將索引為n的區域性變數表的內容壓入運算元棧,由於少了運算元(直接在指令中包含),所以執行速度較快。

將一個資料從運算元棧載入到區域性變數表:

istore、lstore、fstore、dstore、astore,可以帶wide擴充套件,其後接的數值為儲存資料的區域性變數表的索引。

istore_n、lstore_n、fstore_n、dstore_n、astore_n(n=0、1、2、3),代表將運算元儲存到索引為n的區域性變數表中。

將一個常量載入到運算元棧中:
iconst_m1:int型常量-1進棧。

iconst_n:int型常量n進棧(n=0、1、2、3、4、5)

lconst_n:long型常量值n進棧(n=0、1)

fconst_n:float型常量值n進棧(n=0、1、2)

dconst_n:double型常量值n進棧(n=0、1)

以下指令後接的均為運算元在常量池中的下標,常量池的記憶體空間類似於陣列

bipush:byte型常量進棧

sipush:short型常量進棧

ldc:將int、float、String型常量從常量池中推進棧

ldc_w:將int、float、String型常量從常量池中推進棧,為寬索引(意味著在常量池中,該資料較大,一個位元組儲存不了)

ldc2_w:將long、double型常量從常量池中推進棧,為寬索引

aconst_null:null進棧

將一個陣列元素壓進棧中(應該是從堆中取出元素)

iaload:int型陣列的指定下標處的值進棧。

laload:long型陣列的指定下標處的值進棧。

faload:float型陣列的指定下標處的值進棧。

daload:double型陣列的指定下標處的值進棧。

aaload:棧頂的陣列索引、陣列引用先後出棧,並根據這兩個值取出對應的陣列元素值進棧,若引用為null或是索引大於陣列大小,會丟擲異常。從棧中取出的引數將作為index(指定下標),arrayref(指定陣列引用)的值

將一個運算元從棧中存入到堆中的陣列的對應索引處:

iastore:將棧頂int型資料存入指定的陣列下標處。

lastore:將棧頂long型資料存入指定的陣列下標處。

fastore:將棧頂float型資料存入指定的陣列下標處。

dastore:將double型資料存入指定的陣列下標處。

aastore:有三個引數index(int型資料,指定下標)、arrayref(對陣列的引用)、value(引用型別),從棧中退出三個量,分別作為這三個變數的值,該指令主要用來將引用型別資料存入引用陣列中。

bastore:將boolean或是byte型資料存入指定陣列下標處

castore:將char型資料存入指定陣列下標處。

sastore:將short型資料存入指定陣列下標處。

棧相關的基本操作

pop:棧頂數值出棧(準確來說應該是彈出一個儲存單元),該數值型別不能是long或是double型(由於棧中的一個儲存單元佔用32位,而這兩者為64位的資料)

pop2:彈出棧頂的兩個儲存單元,若不是long和double型別資料,則有兩個資料。

dup:複製棧頂數值,並將複製值進棧。

dup_x1:複製棧頂數值,並且將複製值進棧兩次。

dup_x2:複製棧頂數值,並且將複製值壓進棧兩次或是三次。

dup2:複製棧頂兩個儲存單元的值,並將複製值壓進棧。

dup2_x1:複製棧頂兩個儲存單元的值,並且將複製值壓進棧兩次。

dup2_x2:複製棧頂兩個儲存單元的值,並將複製值壓進棧兩次或是三次。

算數運算操作

swap:棧頂的兩個數值交換(要求不能是long、double型)

(i、l、f、d)add:棧頂兩個資料相加,並將結果壓入棧

(i、l、f、g)sub:棧頂兩個資料相減,並將結果壓入棧

(i、l、f、d)mul:棧頂兩資料相乘,並將結果壓入棧

(i、l、f、d)div:棧頂兩資料相除,並將結果壓入棧

(i、l、f、d)rem:棧頂兩資料取模,並將結果壓入棧

(i、l、f、d)neg:棧頂資料取反,並將結果壓入棧

(i、l)shl:將資料按對應位數左移,並將結果壓入棧

(i、l)shr:將資料按對應位數右移,並將結果壓入棧

(i、l)ushr:將無符號int或是long資料右移指定位數,並將結果壓入棧

(i、l)and:棧頂兩個資料按位與,並將結果壓入棧

(i、l)or:棧頂兩個資料按位或,並將結果壓入棧

(i、l)xor:棧頂兩個資料按位異或,並將結果壓入棧

iinc:指定int型變數增加指定值

型別轉換:

i2(l、f、b、d、c、s):將棧頂的int型資料轉換成對應型別並將結果壓入棧,其中的b表示byte型別

l2(i、f、d):將棧頂的long型資料轉換成對應型別並將結果壓入棧。

f2(i、l、d):將棧頂的float型資料轉換成對應型別並將結果壓入棧。

d2(i、l、f):將棧頂的double型資料轉換成對應型別並將結果壓入棧。

比較操作:
lcmp:比較棧頂兩個long型資料大小,並將結果-1、0、1壓入棧內。

fcmpl:比較棧頂兩個float型資料大小,並將結果-1、0、1壓入棧內,當其中一個值為NaN(not a number,表示不明確的數值結果)時,將-1壓入棧

fcmpg:比較棧頂兩個float型資料大小,並將結果-1、0、1壓入棧內,當其中一個值為NaN時,將1壓入棧。

dcmpl:比較棧頂兩個double型資料大小,並將結果-1、0、1壓入棧內,當其中一個值為NaN時,將-1壓入棧。

dcmpg:比較棧頂兩個double型資料大小,並將結果-1、0、1壓入棧內,當其中一個值為NaN時,將1壓入棧。

條件判斷語句

以下語句後接跳轉到的語句的位置

iflt:當棧頂int型數值小於0時跳轉。

ifge:當棧頂int型數值大於等於0時跳轉。

ifgt:當棧頂int型數值大於0時跳轉。

ifle:當棧頂int型數值小於等於0時跳轉。

if_icmpeq:比較棧頂兩個int型數值的大小,當結果等於0時跳轉。

if_icmpne:比較棧頂兩個int型數值的大小,當結果不等於0時跳轉。

if_icmplt:比較棧頂兩個int型數值的大小,當結果小於0時跳轉。

if_icmpge:比較棧頂兩個int型資料的大小,當結果大於等於0時跳轉。

if_icmple:比較棧頂兩個int型資料的大小,當結果大於0時跳轉。

if_acmpeq:比較棧頂兩引用型資料的大小,當結果相等時跳轉。(應該是判斷兩者指向的例項物件是否相等)

if_acmpne:比較棧頂兩引用型資料的大小,當結果不相等時跳轉。

goto:無條件跳轉。

goto_w:無條件跳轉(寬索引)。

ifnull:為null時跳轉。

ifnonnull:不為null時跳轉。

在java SE 7或是更高版本中,jsr、ret、jsr_w、ret_w已經禁用。

tableswitch:用於switch條件跳轉,case值是連續的情況,(可變長度指令)

lookupswitch:用於switch條件跳轉,case值不連續的情況,(可變長度指令)

返回語句:

(i、l、f、d、a)return:從當前方法返回對應型別的資料(a表示引用型別)

return:從當前方法返回void

類相關操作:

getstatic:獲取指定類的靜態域,並將結果壓入棧,其後接一個符號引用

putstatic:為指定的類的靜態域賦值。

getfield:獲取指定類的例項域,並將結果壓入棧。

putfield:給指定類的例項域賦值。

invokevirtual:呼叫例項方法。

invokespecial:呼叫父類的構造方法、例項初始化方法、私有方法。

invokestatic:呼叫靜態方法。

invokeinterface:呼叫介面方法。

new:建立一個例項物件,並將引用值壓入棧頂(我個人的理解是隻是申請了記憶體空間,但具體呼叫什麼構造器由接下來的invokevirtual來決定)。

newarray:建立一個基本資料型別的陣列,並將其引用值壓入棧頂。

arraylength:獲取陣列長度並壓入值並壓入棧頂。

instanceof:檢驗物件是否是指定類的例項,將結果壓入棧,1表示是,0表示不是。

monitorenter:獲取物件的鎖,用於同步方法或同步塊(多執行緒設計中用到)。

multianewarrary:建立指定型別和指定維度的多維陣列(執行該指令時,運算元棧中必須包含各維度的長度值),並將引用值壓入棧。

異常檢測

athrow:將棧頂異常丟擲。

checkcast:檢驗型別轉換,檢驗未通過則丟擲ClassCastException。

關於棧的相關操作,除了指明覆制外,其餘均會從棧中取出資料(不是複製出資料),將區域性變數表中的資料壓入運算元棧壓入的是複製值。