天天看點

Java二進制指令代碼解析

小注:去年在看《深入解析jvm》書的時候做的一些記錄,同時參考了《java虛拟機規範》。隻是對指令的一些列舉,加入了一些自己的了解。可以用來查詢。

java源碼在運作之前都要編譯成為位元組碼格式(如.class檔案),然後由classloader将位元組碼載入運作。在位元組碼檔案中,指令代碼隻是其中的一部分,裡面還記錄了位元組碼檔案的編譯版本、常量池、通路權限、所有成員變量和成員方法等資訊(詳見java位元組碼格式詳解)。本文主要簡單介紹不同java指令的功能以及在代碼中如何解析二進制指令。 

java指令是基于棧的體系結構,大部分的指令預設的操作數在棧中。映像中arm是基于寄存器的操作指令,而x86好像是混合寄存器和存儲器的,發現基于棧的操作指令确實簡單,學起來很快。不過不知道這種操作的效率怎麼樣,以我自己的推測應該是不太好的。對這方面不太了解,随便扯幾句。 

java總共有200多條指令,不過很多都是重複的。我的了解,網絡是java一個非常重要的特性,而且java在設計之初就認為位元組碼是要在網絡中傳輸的,為了減少網絡傳輸流量,位元組碼就要盡量設計精簡、緊湊。因而java增加了很多重複指令,比如盡量減少操作數,因而我們會發現java的很多指令都是沒有操作數的;并且指令中的操作數基本上都是當無法将值放到棧中的資料,比如局部變量的索引号和常量池中的索引号。 

還有一點需要注意的是,在運作過程中,所有boolean、byte、char、short都是以int類型值存在,因而對這些類型的指令操作很少。然而好像sun實作的虛拟機中。這些類型的數組據說不是以int類型的形式儲存的,這個很奇怪。我的了解,以字對齊方式的操作效率會比較高,因而做了這種轉換,以空間換時間。 

常量入棧指令

操作碼(助記符)

操作數

描述(棧指操作數棧)

aconst_null

null值入棧。

iconst_m1

-1(int)值入棧。

iconst_0

0(int)值入棧。

iconst_1

1(int)值入棧。

iconst_2

2(int)值入棧。

iconst_3

3(int)值入棧。

iconst_4

4(int)值入棧。

iconst_5

5(int)值入棧。

lconst_0

0(long)值入棧。

lconst_1

1(long)值入棧。

fconst_0

0(float)值入棧。

fconst_1

1(float)值入棧。

fconst_2

2(float)值入棧。

dconst_0

0(double)值入棧。

dconst_1

1(double)值入棧。

bipush

valuebyte

valuebyte值帶符号擴充成int值入棧。

sipush

valuebyte1

valuebyte2

(valuebyte1 << 8) | valuebyte2 值帶符号擴充成int值入棧。

ldc

indexbyte1

常量池中的常量值(int, float, string reference, object reference)入棧。

ldc_w

indexbyte2

常量池中常量(int, float, string reference, object reference)入棧。

ldc2_w

常量池中常量(long, double)入棧。

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

(wide)aload

indexbyte

從局部變量indexbyte中裝載引用類型值入棧。

aload_0

從局部變量0中裝載引用類型值入棧。

aload_1

從局部變量1中裝載引用類型值入棧。

aload_2

從局部變量2中裝載引用類型值入棧。

aload_3

從局部變量3中裝載引用類型值入棧。

(wide)iload

從局部變量indexbyte中裝載int類型值入棧。

iload_0

從局部變量0中裝載int類型值入棧。

iload_1

從局部變量1中裝載int類型值入棧。

iload_2

從局部變量2中裝載int類型值入棧。

iload_3

從局部變量3中裝載int類型值入棧。

(wide)lload

從局部變量indexbyte中裝載long類型值入棧。

lload_0

lload_1

lload_2

lload_3

(wide)fload

從局部變量indexbyte中裝載float類型值入棧。

fload_0

從局部變量0中裝載float類型值入棧。

fload_1

從局部變量1中裝載float類型值入棧。

fload_2

從局部變量2中裝載float類型值入棧。

fload_3

從局部變量3中裝載float類型值入棧。

(wide)dload

從局部變量indexbyte中裝載double類型值入棧。

dload_0

從局部變量0中裝載double類型值入棧。

dload_1

從局部變量1中裝載double類型值入棧。

dload_2

從局部變量2中裝載double類型值入棧。

dload_3

從局部變量3中裝載double類型值入棧。

aaload

從引用類型數組中裝載指定項的值。

iaload

從int類型數組中裝載指定項的值。

laload

從long類型數組中裝載指定項的值。

faload

從float類型數組中裝載指定項的值。

daload

從double類型數組中裝載指定項的值。

baload

從boolean類型數組或byte類型數組中裝載指定項的值(先轉換為int類型值,後壓棧)。

caload

從char類型數組中裝載指定項的值(先轉換為int類型值,後壓棧)。

saload

從short類型數組中裝載指定項的值(先轉換為int類型值,後壓棧)。

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

(wide)astore

将棧頂引用類型值儲存到局部變量indexbyte中。

astroe_0

将棧頂引用類型值儲存到局部變量0中。

astore_1

将棧頂引用類型值儲存到局部變量1中。

astore_2

将棧頂引用類型值儲存到局部變量2中。

astore_3

将棧頂引用類型值儲存到局部變量3中。

(wide)istore

将棧頂int類型值儲存到局部變量indexbyte中。

istore_0

将棧頂int類型值儲存到局部變量0中。

istore_1

将棧頂int類型值儲存到局部變量1中。

istore_2

将棧頂int類型值儲存到局部變量2中。

istore_3

将棧頂int類型值儲存到局部變量3中。

(wide)lstore

将棧頂long類型值儲存到局部變量indexbyte中。

lstore_0

将棧頂long類型值儲存到局部變量0中。

lstore_1

将棧頂long類型值儲存到局部變量1中。

lstore_2

将棧頂long類型值儲存到局部變量2中。

lstroe_3

将棧頂long類型值儲存到局部變量3中。

(wide)fstore

将棧頂float類型值儲存到局部變量indexbyte中。

fstore_0

将棧頂float類型值儲存到局部變量0中。

fstore_1

将棧頂float類型值儲存到局部變量1中。

fstore_2

将棧頂float類型值儲存到局部變量2中。

fstore_3

将棧頂float類型值儲存到局部變量3中。

(wide)dstore

将棧頂double類型值儲存到局部變量indexbyte中。

dstore_0

将棧頂double類型值儲存到局部變量0中。

dstore_1

将棧頂double類型值儲存到局部變量1中。

dstore_2

将棧頂double類型值儲存到局部變量2中。

dstore_3

将棧頂double類型值儲存到局部變量3中。

aastore

将棧頂引用類型值儲存到指定引用類型數組的指定項。

iastore

将棧頂int類型值儲存到指定int類型數組的指定項。

lastore

将棧頂long類型值儲存到指定long類型數組的指定項。

fastore

将棧頂float類型值儲存到指定float類型數組的指定項。

dastore

将棧頂double類型值儲存到指定double類型數組的指定項。

bastroe

将棧頂boolean類型值或byte類型值儲存到指定boolean類型數組或byte類型數組的指定項。

castore

将棧頂char類型值儲存到指定char類型數組的指定項。

sastore

将棧頂short類型值儲存到指定short類型數組的指定項。

wide指令

wide

使用附加位元組擴充局部變量索引(iinc指令特殊)。

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

nop

空操作。

pop

從棧頂彈出一個字長的資料。

pop2

從棧頂彈出兩個字長的資料。

dup

複制棧頂一個字長的資料,将複制後的資料壓棧。

dup_x1

複制棧頂一個字長的資料,彈出棧頂兩個字長資料,先将複制後的資料壓棧,再将彈出的兩個字長資料壓棧。

dup_x2

複制棧頂一個字長的資料,彈出棧頂三個字長的資料,将複制後的資料壓棧,再将彈出的三個字長的資料壓棧。

dup2

複制棧頂兩個字長的資料,将複制後的兩個字長的資料壓棧。

dup2_x1

複制棧頂兩個字長的資料,彈出棧頂三個字長的資料,将複制後的兩個字長的資料壓棧,再将彈出的三個字長的資料壓棧。

dup2_x2

複制棧頂兩個字長的資料,彈出棧頂四個字長的資料,将複制後的兩個字長的資料壓棧,再将彈出的四個字長的資料壓棧。

swap

交換棧頂兩個字長的資料的位置。java指令中沒有提供以兩個字長為機關的交換指令。

類型轉換指令

i2f

将棧頂int類型值轉換為float類型值。

i2l

将棧頂int類型值轉換為long類型值。

i2d

将棧頂int類型值轉換為double類型值。

f2i

将棧頂float類型值轉換為int類型值。

f2l

将棧頂float類型值轉換為long類型值。

f2d

将棧頂float類型值轉換為double類型值。

l2i

将棧頂long類型值轉換為int類型值。

l2f

将棧頂long類型值轉換為float類型值。

l2d

将棧頂long類型值轉換double類型值。

d2i

将棧頂double類型值轉換為int類型值。

d2f

将棧頂double類型值轉換為float類型值。

d2l

将棧頂double類型值轉換為long類型值。

i2b

将棧頂int類型值截斷成byte類型,後帶符号擴充成int類型值入棧。

i2c

将棧頂int類型值截斷成char類型值,後帶符号擴充成int類型值入棧。

i2s

将棧頂int類型值截斷成short類型值,後帶符号擴充成int類型值入棧。

整數運算

iadd

将棧頂兩int類型數相加,結果入棧。

isub

将棧頂兩int類型數相減,結果入棧。

imul

将棧頂兩int類型數相乘,結果入棧。

idiv

将棧頂兩int類型數相除,結果入棧。

irem

将棧頂兩int類型數取模,結果入棧。

ineg

将棧頂int類型值取負,結果入棧。

ladd

将棧頂兩long類型數相加,結果入棧。

lsub

将棧頂兩long類型數相減,結果入棧。

lmul

将棧頂兩long類型數相乘,結果入棧。

ldiv

将棧頂兩long類型數相除,結果入棧。

lrem

将棧頂兩long類型數取模,結果入棧。

lneg

将棧頂long類型值取負,結果入棧。

(wide)iinc

constbyte

将整數值constbyte加到indexbyte指定的int類型的局部變量中。

浮點運算

fadd

将棧頂兩float類型數相加,結果入棧。

fsub

将棧頂兩float類型數相減,結果入棧。

fmul

将棧頂兩float類型數相乘,結果入棧。

fdiv

将棧頂兩float類型數相除,結果入棧。

frem

将棧頂兩float類型數取模,結果入棧。

fneg

将棧頂float類型值取反,結果入棧。

dadd

将棧頂兩double類型數相加,結果入棧。

dsub

将棧頂兩double類型數相減,結果入棧。

dmul

将棧頂兩double類型數相乘,結果入棧。

ddiv

将棧頂兩double類型數相除,結果入棧。

drem

将棧頂兩double類型數取模,結果入棧。

dneg

将棧頂double類型值取負,結果入棧。

邏輯運算——移位運算

ishl

左移int類型值。

lshl

左移long類型值。

ishr

算術右移int類型值。

lshr

算術右移long類型值。

iushr

邏輯右移int類型值。

lushr

邏輯右移long類型值。

邏輯運算——按位布爾運算

iand

對int類型按位與運算。

land

對long類型的按位與運算。

ior

對int類型的按位或運算。

lor

對long類型的按位或運算。

ixor

對int類型的按位異或運算。

lxor

對long類型的按位異或運算。

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

ifeq

branchbyte1

branchbyte2

若棧頂int類型值為0則跳轉。

ifne

若棧頂int類型值不為0則跳轉。

iflt

若棧頂int類型值小于0則跳轉。

ifle

若棧頂int類型值小于等于0則跳轉。

ifgt

若棧頂int類型值大于0則跳轉。

ifge

若棧頂int類型值大于等于0則跳轉。

if_icmpeq

若棧頂兩int類型值相等則跳轉。

if_icmpne

若棧頂兩int類型值不相等則跳轉。

if_icmplt

若棧頂兩int類型值前小于後則跳轉。

if_icmple

若棧頂兩int類型值前小于等于後則跳轉。

if_icmpgt

若棧頂兩int類型值前大于後則跳轉。

if_icmpge

若棧頂兩int類型值前大于等于後則跳轉。

ifnull

若棧頂引用值為null則跳轉。

ifnonnull

若棧頂引用值不為null則跳轉。

if_acmpeq

若棧頂兩引用類型值相等則跳轉。

if_acmpne

若棧頂兩引用類型值不相等則跳轉。

控制流指令——比較指令

lcmp

比較棧頂兩long類型值,前者大,1入棧;相等,0入棧;後者大,-1入棧。

fcmpl

比較棧頂兩float類型值,前者大,1入棧;相等,0入棧;後者大,-1入棧;有nan存在,-1入棧。

fcmpg

dcmpl

比較棧頂兩double類型值,前者大,1入棧;相等,0入棧;後者大,-1入棧;有nan存在,-1入棧。

dcmpg

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

goto

無條件跳轉到指定位置。

goto_w

branchbyte3

branchbyte4

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

控制流指令——表跳轉指令

tableswitch

<0-3bytepad>

defaultbyte1

defaultbyte2

defaultbyte3

defaultbyte4

lowbyte1

lowbyte2

lowbyte3

lowbyte4

highbyte1

highbyte2

highbyte3

highbyte4

jump offsets...

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

lookupswitch

npairs1

npairs2

npairs3

npairs4

match offsets

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

控制流指令——異常和finally

athrow

抛出異常。

jsr

跳轉到子例程式。

jsr_w

跳轉到子例程式(寬索引)。

(wide)ret

傳回子例程式。

對象操作指令

new

建立新的對象執行個體。

checkcast

類型強轉。

instanceof

判斷類型。

getfield

擷取對象字段的值。

putfield

給對象字段指派。

getstatic

擷取靜态字段的值。

putstatic

給靜态字段指派。

數組操作指令

newarray

atype

建立type類型的數組。

anewarray

建立引用類型的數組。

arraylength

擷取一維數組的長度。

multianewarray

dimension

建立dimension次元的數組。

方法調用指令

invokespecial

編譯時方法綁定調用方法。

invokevirtual

運作時方法綁定調用方法。

invokestatic

調用靜态方法。

invokeinterface

count

調用接口方法。

方法傳回指令

ireturn

傳回int類型值。

lreturn

傳回long類型值。

freturn

傳回float類型值。

dreturn

傳回double類型值。

areturn

傳回引用類型值。

return

void函數傳回。

線程同步指令

monitorenter

進入并獲得對象螢幕。

monitorexit

釋放并退出對象螢幕。