程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> JAVA 字節碼指令基礎教程

JAVA 字節碼指令基礎教程

編輯:關於JAVA
 

介紹java虛擬機的指令功能,至少能閱讀java代碼生成的字節碼指令含義

一、概述

Java虛擬機采用基於棧的架構,其指令由操作碼和操作數組成。

  • 操作碼:一個字節長度(0~255),意味著指令集的操作碼個數不能操作256條。
  • 操作數:一條指令可以有零或者多個操作數,且操作數可以是1個或者多個字節。編譯後的代碼沒有采用操作數長度對齊方式,比如16位無符號整數需使用兩個字節儲存(假設為byte1和byte2),那麼真實值是 (byte1 << 8) | byte2

放棄操作數對齊操作數對齊方案:

  • 優勢:可以省略很多填充和間隔符號,從而減少數據量,具有更高的傳輸效率;Java起初就是為了面向網絡、智能家具而設計的,故更加注重傳輸效率。
  • 劣勢:運行時從字節碼裡構建出具體數據結構,需要花費部分CPU時間,從而導致解釋執行字節碼會損失部分性能。

二、指令

大多數指令包含了其操作所對應的數據類型信息,比如iload,表示從局部變量表中加載int型的數據到操作數棧;而fload表示加載float型數據到操作數棧。由於操作碼長度只有1Byte,因此Java虛擬機的指令集對於特定操作只提供有限的類型相關指令,並非為每一種數據類型都有相應的操作指令。必要時,有些指令可用於將不支持的類型轉換為可被支持的類型。

對於byte,short,char,boolean類型,往往沒有單獨的操作碼,通過編譯器在編譯期或者運行期將其擴展。對於byte,short采用帶符號擴展,chart,boolean采用零位擴展。相應的數組也是采用類似的擴展方式轉換為int類型的字節碼來處理。 下面分門別類來介紹Java虛擬機指令,都以int類型的數據操作為例。

棧是指操作數棧

2.1 棧操作相關

load和store

  • load 命令:用於將局部變量表的指定位置的相應類型變量加載到棧頂;
  • store命令:用於將棧頂的相應類型數據保入局部變量表的指定位置;
變量進棧 含義 變量保存 含義 iload 第1個int型變量進棧 istore 棧頂nt數值存入第1局部變量 iload_0 第1個int型變量進棧 istore_0 棧頂int數值存入第1局部變量 iload_1 第2個int型變量進棧 istore_1 棧頂int數值存入第2局部變量 iload_2 第3個int型變量進棧 istore_2 棧頂int數值存入第3局部變量 iload_3 第4個int型變量進棧 istore_3 棧頂int數值存入第4局部變量         lload 第1個long型變量進棧 lstore 棧頂long數值存入第1局部變量 fload 第1個float型變量進棧 fstore 棧頂float數值存入第1局部變量 dload 第1個double型變量進棧 dstore 棧頂double數值存入第1局部變量 aload 第1個ref型變量進棧 astore 棧頂ref對象存入第1局部變量

const、push和ldc

  • const、push:將相應類型的常量放入棧頂
  • ldc:則是從常量池中將常量
常量進棧 含義 aconst_null null進棧 iconst_m1 int型常量-1進棧 iconst_0 int型常量0進棧 iconst_1 int型常量1進棧 iconst_2 int型常量2進棧 iconst_3 int型常量3進棧 iconst_4 int型常量4進棧 iconst_5 int型常量5進棧     lconst_0 long型常量0進棧 fconst_0 float型常量0進棧 dconst_0 double型常量0進棧     bipush byte型常量進棧 sipush short型常量進棧 常量池操作 含義 ldc int、float或String型常量從常量池推送至棧頂 ldc_w int、float或String型常量從常量池推送至棧頂(寬索引) ldc2_w long或double型常量從常量池推送至棧頂(寬索引)

pop和dup

  • pop用於棧頂數值出棧操作;
  • dup用於賦值棧頂的指定個數的數值,並將其壓入棧頂指定次數;
棧頂操作 含義 pop 棧頂數值出棧(不能是long/double) pop2 棧頂數值出棧(long/double型1個,其他2個)     dup 復制棧頂數值,並壓入棧頂 dup_x1 復制棧頂數值,並壓入棧頂2次 dup_x2 復制棧頂數值,並壓入棧頂3次 dup2 復制棧頂2個數值,並壓入棧頂 dup2_x1 復制棧頂2個數值,並壓入棧頂2次 dup2_x2 復制棧頂2個數值,並壓入棧頂3次     swap 棧頂的兩個數值互換,且不能是long/double

注意:dup2對於long、double類型的數據就是一個,對於其他類型的數據,才是真正的兩個,這個的2代表的是2個slot的數據。

2.2 對象相關

字段調用

字段調用 含義 getstatic 獲取類的靜態字段,將其值壓入棧頂 putstatic 給類的靜態字段賦值 getfield 獲取對象的字段,將其值壓入棧頂 putfield 給對象的字段賦值

方法調用

方法調用 作用 解釋 invokevirtual 調用實例方法 虛方法分派 invokestatic 調用類方法 static方法 invokeinterface 調用接口方法 運行時搜索合適方法調用 invokespecial 調用特殊實例方法 包括實例初始化方法、父類方法 invokedynamic 由用戶引導方法決定 運行時動態解析出調用點限定符所引用方法

方法返回

方法返回 含義 ireturn 當前方法返回int lreturn 當前方法返回long freturn 當前方法返回float dreturn 當前方法返回double areturn 當前方法返回ref

對象和數組

  • 創建類實例: new
  • 創建數組:newarray、anewarray、multianewarray
  • 數組元素 加載到 操作數棧:xaload (x可為b,c,s,i,l,f,d,a)
  • 操作數棧的值 存儲到數組元素: xastore (x可為b,c,s,i,l,f,d,a)
  • 數組長度:arraylength
  • 類實例類型:instanceof、checkcast

2.3 運算指令

運算指令是用於對操作數棧上的兩個數值進行某種運算,並把結果重新存入到操作棧頂。Java虛擬機只支持整型和浮點型兩類數據的運算指令,所有指令如下:

運算 int long float double 加法 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
  • 自增:iin
  • 比較:dcmpg,dcmpl,fcmpg,fcmpl,lcmp

2.4 類型轉換

類型轉換用於將兩種不同類型的數值進行轉換。

(1) 對於寬化類型轉換(小范圍向大范圍轉換),無需顯式的轉換指令,並且是安全的操作。各種范圍從小到大依次排序: int, long, float, double。

(2)對於窄化類型轉換,必須顯式地調用類型轉換指令,並且該過程很可能導致精度丟失。轉換規則中需要特別注意的是當浮點值為NaN, 則轉換結果為int或long的0。雖然窄化運算可能會發生上/下限溢出和精度丟失等情況,但虛擬機規范明確規定窄化轉換U不可能導致虛擬機拋出異常。

類型轉換指令:i2b, i2c,f2i等等。

2.5 流程控制

控制指令是指有條件或無條件地修改PC寄存器的值,從而達到控制流程的目標

  • 條件分支:ifeq、iflt、ifnull、ifnonnull等
  • 復合分支:tableswitch、lookupswitch
  • 無條件分支:goto、goto_w、jsr、jsr_w、ret

2.6 同步與異常

異常:

Java程序顯式拋出異常: athrow指令。在Java虛擬機中,處理異常(catch語句)不是由字節碼指令來實現,而是采用異常表來完成。

同步:

方法級的同步和方法內部分代碼的同步,都是依靠管程(Monitor)來實現的。

Java語言使用synchronized語句塊,那麼Java虛擬機的指令集中通過monitorenter和monitorexit兩條指令來完成synchronized的功能。為了保證monitorenter和monitorexit指令一定能成對的調用(不管方法正常結束還是異常結束),編譯器會自動生成一個異常處理器,該異常處理器的主要目的是用於執行monitorexit指令。

2.7 小結

在基於堆棧的的虛擬機中,指令的主戰場便是操作數棧,除了load是從局部變量表加載數據到操作數棧以及store儲存數據到局部變量表,其余指令基本都是用於操作數棧的。

 
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved