JAVA基礎回顧-字節碼指令

引言

眾所周知,Java程序是運行在Java虛擬機上的,而這里的“虛擬”是對什么東西進行虛擬呢?答案當然就是對“實體”機進行虛擬啦,虛擬機可以看做是對實體機進行了進一步的封裝和抽象,隱藏了不同實體機之間的差別,從而達成“Write Once,Run AnyWhere”的目標。既然虛擬機是對實體機的虛擬,所以我認為虛擬機和實體機在結構和功能上必然存在某種程度上的對應與關聯。因此我們在學習時應該注意發掘和類比兩者之間的關系。
本著這樣的思想,我們進行Java字節碼指令的學習。JAVA字節碼在JAVA虛擬機中的地位相當于實體機的機器碼,一切在Java虛擬機上運行的程序都要被解釋或編譯成字節碼,一切在實體機上運行的程序最后也都要編譯成機器碼。Java字節碼指令可以對字節碼進行操作,在實體機中對機器碼進行操作的是匯編語言。所以Java字節碼指令對應匯編語言,Java字節碼指令集對應匯編指令集。

字節碼簡介

Java字節碼指令由一個字節長度的,代表某種特定操作含義的數字(操作碼)以及其后的零至多個代表此操作所需參數(操作數)。此外字節碼指令是面向操作數棧的,這里操作數棧在功能上對應實體機的寄存器但是結構上有所區別。

字節碼與數據類型

在字節碼指令集中,大多數指令都對應的其操作所對應的數據類型信息,比如iload表示從局部變量表中加載int型的數據到操作棧中,fload從局部變量表中加載float型的數據到操作棧中...但是由于Java字節碼的操作碼只有一個字節(即0~255),這意味著指令集的操作碼總數不可能超過256條。所以如果要求Java運行時所有的數據類型都有對應的與數據類型相關的指令去支持的話,操作碼的總數將超過256條。所以JAVA字節碼指令集被設計為Not Orthogonal(非完全獨立),即并非每種數據類型和每種操作都有對應的指令,有一些指令可以在必要的時候將一些不被支持的數據類型轉換為被支持的數據類型。我們可以以數據類型為列,操作指令為行制作一張表,其中為空的項即說明虛擬機不支持對這種數據類型進行這項操作。


加載和存儲指令

加載和存儲指令用于將數據在幀棧中的局部變量表和操作數棧之間傳輸。

  • 將一個局部變量表加載到操作數棧:
    iload、iload_<n>、lload、lload_<n>、fload、fload_<n>、dload、dload_<n>、aload、aload_<n>
  • 將一個數值從操作數棧儲存到局部變量表:
    istore,istore_<n>,lstore,lstore_<n>,fstore,fstore_<n>,dstore,dstore_<n>,astore,astore_<n>
  • 將一個常量加載到操作數棧:
bipush,sipush,lde,lde_w,ldc2_w,aconst_null,iconst_ml,iconst_<i>,lconst_<i>,fconst_<i>,dconst_<i>。
  • 拓充局部變量表的訪問引索的指令:wide

運算指令

運算指令用于對操作數棧上的值進行某種特定的運算。

  • 加法運算:iadd,ladd,fadd,dadd。
  • 減法運算:isub,lsub,fsub,dsub。
  • 乘法運算:imul,lmul,fmul,dmul。
  • 除法運算:idiv,ldiv,fdiv,ddiv。
  • 求余指令:irem,lrem,frem,drem。
  • 取反指令:imeg,lmeg,fmeg,dmeg。
  • 位移指令:ishl,ishr,iushr,lshl,lshr,lushr。
  • 按位或指令:ior,lor。
  • 按位與指令:iand,land。
  • 按位異或指令:ixor,lxor。
  • 局部變量自增指令:iinc。
  • 比較指令:dcmpg,dcmpl,fcmpg,fcmpl,lcmp。
    注:只有在除法指令(idiv,ldiv)和求余指令(irem,lrem)當出現除數為零時會導致虛擬機拋出AirtmeticException異常,其余整形和浮點型運算場景都不會拋出異常

類型轉換指令

類型轉換指令可以將兩種不同數值類型進行相互轉換。
Java虛擬機天然支持基本數據類型的寬化類型轉換,例如int到long、flost、double等。
對于窄化數據類型轉化則必須用顯示的轉換指令:

  • i2b(int -> boolean)
  • i2c(int -> char)
  • i2s(int -> short)
  • l2i(long -> int)
  • f2i(float -> int)
  • f2l(float -> long)
  • d2i(double -> int)
  • d2l(double -> long)
  • d2f(double -> float)
    幾點說明:
  • int/long 類型窄化轉換為整數類型T時,轉換過程為丟棄除最低位N(T的數據類型長度)個字節以外的內容。
  • 浮點值窄化轉換為整數類型T(int/long)時:
if(浮點值==NaN){
    result = 0;
}else{
    value = [浮點值];  //向下取整
    if(T.min <= value <= T.max){    //value在T的表示范圍內
        result = value;
    }else{
        if(value > 0) result = T.max;
        if(value < 0) result = T.min;
    }
}

對象創建與訪問指令

  • 創建類實例的指令:new
  • 創建數組的指令:newarray,anewarray,multianewarray
  • 訪問類字段(static字段)和實例字段(非static字段)的指令:getfield,putfield,getstatic,putstatic
  • 將一個數組元素加載到操作數棧的指令:
    baload,caload,saload,iaload,faload,daload,aaload
  • 將一個操作數棧的值存儲到數組元素中的指令:
    bastore,castore,iastore,sastore,fastore,fastore,dastore,aastore
  • 取數組長度的指令:arraylength
  • 檢查類實例類型的指令:instanceof,checkcast

操作數棧管理指令

  • 將一個操作數棧的棧頂一個或兩個元素出棧:pop、pop2。
  • 復制棧頂一個或兩個數值并將復制值或雙份的復制值重新壓入棧頂:dup、dup2、dup_x1,dup2_x1,dup_x2,dup2_x2。
  • 將棧頂端的兩個數值交換:swap。

控制轉移指令

控制轉移指令可以讓Java虛擬機有條件或者無條件的從指定的位置而不是控制轉移指令的下一條指令繼續執行程序。

  • 條件分支:
    ifeq,ifit,ifle,ifgt,ifnull,ifnonnull,if_icmpeq,if_icmpne,if_icmplt,if_icmpgt,if_icmple,if_icmpge,if_acmpeq,if_acmpne。
  • 復合條件分支:tableswitch,lookupswitch。
  • 無條件分支:gosto,goto_w,jsr,jsr_w,ret。

方法調用和返回指令

  • invokevirtual:用于調用對象的實例方法,根據對象的實際類型進行分派(虛方法分派)。
  • invokeinterface:用于調用接口方法,它在運行時搜索一個實現了這個接口方法的對象,找出適合的方法進行調用。
  • invokespecial:用于調用一些需要特殊處理的實例方法,包括實例的初始化方法,私有方法和父類方法。
  • invokestatic:用于調用類方法(static方法)
  • invokedynamic:用于運行時動態解析出調用點限定符所應用的方法,并執行該方法。(前面的分派邏輯都固化在虛擬機內部,而該指令的分派邏輯是由用戶自定義)。
  • 方法返回指令:ireture(返回類型是int,short,byte,char,boolean時),lreturn,freturn,dreturn,areturn,另外還有一條return供void方法、實例/類/接口的初始化方法使用。

異常處理指令

顯式拋出異常指令:athrow

同步指令

  • monitorenter,monitorexit

小練習

我們拿Java里面比較經典的i++和++i問題來做個練習,熟悉下用字節碼分析問題:
Test Case for ++i:

public class Test_1 {
    public static void main( String[] argv )
    {
        int  value = 0;
        value = ++value;

        System.out.println(value);
    }
}

運行結果:1。
對應部分字節碼及分析:

Code:
       0: iconst_0                 //將0加載到棧頂
       1: istore_1                 //將0存儲到變量value
       2: iinc          1, 1       //value在局部變量表自增為1,(此處為虛指令,真實的變量操作要靠load和store指令)
       5: iload_1                  //將value的值加載到棧頂
       6: istore_1                 //將棧頂的內容保存到變量value,value=1
       7: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      10: iload_1
      11: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
      14: return

Test Case for i++:

public class Test {


    public static void main( String[] argv )
    {
       int  value = 0;
       value = value++;

        System.out.println(value);
    }


}

運行結果:0
字節碼及分析:

 Code:
       0: iconst_0              //將0加載到棧頂
       1: istore_1              //將0存儲到變量value
       2: iload_1               //將value的值加載到棧頂,棧頂為0
       3: iinc          1, 1    //value在局部變量表自增為1
       6: istore_1              //將棧頂的內容保存到變量value,value=0
       7: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      10: iload_1
      11: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
      14: return

以上通過字節碼分析對這個問題無疑有了更深層次的理解。

我的博客:博客

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,563評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,694評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,672評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,965評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,690評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,019評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,013評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,188評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,718評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,438評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,667評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,149評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,845評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,252評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,590評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,384評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內容