問題背景
安卓開發過程中,經常要通過看一些java代碼對應的字節碼,來了解java代碼編譯后的運行機制,本文將通過一個簡單的demo介紹一些基本的字節碼指令。
問題分析
比如以下代碼:
public class test {public static void main(String[] args) {int a = 100;int b = 100;int c = 100;a += b + c;System.out.println(a);}
}
運行結果如下:
a += b + c <==> a += (b + c),查看對應的字節碼文件如下:
// class version 52.0 (52)
// access flags 0x21
public class test {// compiled from: test.java// access flags 0x1public <init>()VL0LINENUMBER 1 L0ALOAD 0INVOKESPECIAL java/lang/Object.<init> ()VRETURNL1LOCALVARIABLE this Ltest; L0 L1 0MAXSTACK = 1MAXLOCALS = 1// access flags 0x9public static main([Ljava/lang/String;)VL0LINENUMBER 3 L0BIPUSH 100ISTORE 1 // 將100存儲到局部變量L1LINENUMBER 4 L1BIPUSH 100ISTORE 2 // 將100存儲到局部變量L2LINENUMBER 5 L2BIPUSH 100ISTORE 3 // 將100存儲到局部變量L3LINENUMBER 6 L3ILOAD 1 // 從局部變量表中加載 int 類型到操作數棧ILOAD 2 // 從局部變量表中加載 int 類型到操作數棧ILOAD 3 // 從局部變量表中加載 int 類型到操作數棧IADD // 將棧頂兩個 int 類型數值相加IADD // 將棧頂兩個 int 類型數值相加ISTORE 1 // 將 int 類型存儲到局部變量中,這里就是把結果存儲到第一個變量L4LINENUMBER 7 L4GETSTATIC java/lang/System.out : Ljava/io/PrintStream;ILOAD 1INVOKEVIRTUAL java/io/PrintStream.println (I)VL5LINENUMBER 8 L5RETURNL6LOCALVARIABLE args [Ljava/lang/String; L0 L6 0LOCALVARIABLE a I L1 L6 1LOCALVARIABLE b I L2 L6 2LOCALVARIABLE c I L3 L6 3MAXSTACK = 3MAXLOCALS = 4
}
問題總結
通過查看java代碼對應的字節碼,可以比較清楚的看到java代碼編譯后的執行流程。在安卓開發中,了解字節碼知識還是非常有必要的,在關鍵時刻,我們查看字節碼,能夠很好的解答一些疑惑,下面是常見的一些字節碼指令:
1. 加載和存儲指令:aload:從局部變量表中加載引用類型到操作數棧。astore:將引用類型存儲到局部變量表中。iload:從局部變量表中加載 int 類型到操作數棧。istore:將 int 類型存儲到局部變量表中。fload:從局部變量表中加載 float 類型到操作數棧。fstore:將 float 類型存儲到局部變量表中。2. 算術和邏輯指令:iadd:將棧頂兩個 int 類型數值相加。isub:將棧頂兩個 int 類型數值相減。imul:將棧頂兩個 int 類型數值相乘。idiv:將棧頂兩個 int 類型數值相除。iand:將棧頂兩個 int 類型數值進行按位與操作。ior:將棧頂兩個 int 類型數值進行按位或操作。3. 類型轉換指令:i2l:將 int 類型轉換為 long 類型。l2i:將 long 類型轉換為 int 類型。f2d:將 float 類型轉換為 double 類型。d2i:將 double 類型轉換為 int 類型。4. 控制流指令:if_icmpeq:如果兩個 int 類型數值相等,則跳轉到指定位置。goto:無條件跳轉到指定位置。tableswitch:根據索引值跳轉到不同位置的指令。5. 方法調用和返回指令:invokevirtual:調用實例方法。invokestatic:調用靜態方法。invokeinterface:調用接口方法。ireturn:從方法中返回 int 類型值。invokedynamic: 運行時動態解析并綁定方法調用
持續更新,有興趣的小伙伴可以進一步深入研究。