天天看點

JVM Class詳解之二 Method位元組碼指令JVM有哪些位元組碼指令HelloWorld搞起LineNumberTable

jvm class詳解之一中我們介紹了class檔案的結構和如何使用16進制編輯器讀懂class檔案。

今天我們來繼續一起下class檔案中method方法中經過java編譯器編譯後的method位元組碼指令是什麼樣子的

首先我們需要了解jvm有哪些位元組碼指令

是将本地變量中的資料推送入棧中 (什麼是本地變量我們後面聊)

iload,iload_,lload,lload_,fload,fload_,dload,dload_,aload,aload_

iload_0:将第一個int型本地變量推送至棧頂

iload中i表示為int型(l為long,f為float,d為double ,a為引用類型),load表示動作為load,

後面的指令大多都是這種結構,先是聲明操作數類型,再說明具體動作。

同理 fload:将本地變量的float型資料推送棧頂

load是從本地變量到棧頂,store是從棧頂到本地變量

istore,istore_,lstore,lstore_,fstore,fstore_,dstore,dstor_,astore,astore_

istore:将棧頂int型數值存入制定數組的指定索引位置

除了本地變量到棧頂,還有常量到棧頂

bipush,sipush,ldc,ldc_w,ldc2_w,aconst_null,iconst_ml,iconst_,lconst_,fconst_,dconst_

ldc:将int,float或string型常量從常量池中推送至棧頂

iconst_0:将int型0推送至棧頂

加:iadd,ladd,fadd,dadd  :将棧頂兩個數值相加并将将結果壓入棧頂

減:is ,ls ,fs ,ds 

乘:imul,lmul,fmul,dmul 

除:idiv,ldiv,fdiv,ddiv 

餘數:irem,lrem,frem,drem 

取負:ineg,lneg,fneg,dneg 

移位:ishl,lshr,iushr,lshl,lshr,lushr 

按位或:ior,lor 

按位與:iand,land 

按位異或:ixor,lxor 

類型轉換:i2l,i2f,i2d,l2f,l2d,f2d(放寬數值轉換) 

i2b,i2c,i2s,l2i,f2i,f2l,d2i,d2l,d2f(縮窄數值轉換)

這個很簡單的顧名思義哈哈。

lcmp:比較棧頂兩個long型數值的大小,并将結果(1,0,-1)壓入棧頂

fcmpl:比較棧頂兩個float型數值的大小,并将結果(1,0,-1)壓入棧頂,當其中一個為nan,将-1壓入棧頂

fcmpg:。。。。。其中一個為nan,将1壓入棧頂

dcmpl

dcmpg

ifeq:當棧頂int型數值等于0時跳轉

ifne:當棧頂int型數值不等于0時跳轉

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

ifge:大于等于0

ifgt:大于0

ifle:小于等于0

if_icmpeq:比較棧頂兩個int大小,等于0跳轉

if_icmpne:不等于0跳轉

。。。

goto:無條件跳轉

goto,goto_w,jsr,jsr_w,ret 

ifnull:為null時跳轉

ifnonnull:不為null時跳轉

finally關鍵字的實作使用:jsr,jsr_w,ret

ireturn:從目前方法傳回int

lreturn:從目前方法放回long

return:從目前方法傳回void

getstatic:擷取指定類的靜态域,并将其值壓入棧頂

putstatic:為指定的類的靜态域指派

getfield:擷取指定類的執行個體域,并将其值壓入棧頂

pufield:為指定類的執行個體域指派

invokevirtual:調用執行個體方法

invokespecial:調用超類構造方法,執行個體初始化方法沒有方法

invokestatic:調用靜态方法

invokeinterface:調用接口方法

invokedynamic:調用動态方法

new:建立一個對象

newarray:建立一個指定的原始類型數組

anewarray:建立一個引用型的數組,并将其引用值壓入棧頂

arraylength:擷取數組的長度并壓入棧頂

athrow:将棧頂的異常抛出

checkcast:檢查類型轉換,檢查未通過會抛出classcastexception

instanceof:檢查是否是指定的類的執行個體,如果是,将1壓入棧頂,不是将0壓入棧頂

monitorenter:擷取對象的鎖,用于同步方法或者同步塊

monitorexit:釋放對象的鎖

wide:擴充本地變量的寬度

好至此主要的指令已經介紹完畢,怎麼分類仁者見仁啦。

這個intaddmethod方法傳入兩個int,判斷是否小于0,如果小于傳回-1,都不小于傳回相加值

我們通過javap -verbose helloworldmethod.class 檢視位元組碼指令

JVM Class詳解之二 Method位元組碼指令JVM有哪些位元組碼指令HelloWorld搞起LineNumberTable
JVM Class詳解之二 Method位元組碼指令JVM有哪些位元組碼指令HelloWorld搞起LineNumberTable

我們按照指令一條一條看

0:iload_1:将本地變量中第一個int (a)加載到棧頂

為什麼是a呢,我們再看lovalvariabletable。每個方法都有localvariabletable。是本地變量表。我們可以看到在本地變量表中的第一個int就是a

JVM Class詳解之二 Method位元組碼指令JVM有哪些位元組碼指令HelloWorld搞起LineNumberTable

1:ifge:判斷棧頂的int是否大于0如果大于将1壓入棧頂,如果不大于将0壓入棧頂

分支1:如果目前值不大于0将0壓入棧頂,

分支2:如果目前值大于0跳轉到指令6

4:iconst_m1:将整型-1壓入棧頂 

5:ireturn

6:iload_2:将本地變量彙總第二個int(b)壓入棧頂

7:ifge:判斷棧頂的int是否大于0

分支3:如果目前值不大于0,将棧頂壓入0

分支4:如果目前值大于0,将1壓入棧頂,并跳轉到執行12執行

10:iconst_m1:将int -1壓入棧頂

11:ireturn:傳回棧頂int值

12:iload_1:将本地變量第一個int壓入棧頂(a)

13:iload_2:将本地變量第二個int壓入棧頂(b)

14:iadd:将棧頂的兩個int相加并将結果壓入棧頂 a+b

15:istore_3:将棧頂的int值,存入本地變量表中第三個int,第三個int為c,将結果付給了c

JVM Class詳解之二 Method位元組碼指令JVM有哪些位元組碼指令HelloWorld搞起LineNumberTable

16:iload_3:将本地變量中的第三個int壓入棧頂,取出c

17:ireturn:将棧頂的第一個int傳回

code中 還有另外一個東西

JVM Class詳解之二 Method位元組碼指令JVM有哪些位元組碼指令HelloWorld搞起LineNumberTable

這個是什麼,這個是linenumbertable,其中記錄了編譯出來的位元組碼指令和源碼的對應關系

這個屬性不是很重要。另外就是一個源碼會對應多條指令的

例如源碼中的第5行return -1 ,對應指令為4和5

JVM Class詳解之二 Method位元組碼指令JVM有哪些位元組碼指令HelloWorld搞起LineNumberTable

好了至此我們就知道了我們java檔案編譯後的method中有什麼東西,jvm又是怎樣讀取位元組碼指令做相應操作的了。