Java字節碼

Java 字節碼（Java bytecode）是Java虛擬機執行的一種指令格式。大多數Opcode都是一個字節長，而有些操作需要參數，導致了有一些多字節的操作碼。而且並不是所有可能的256個操作碼都被使用；其中有51個操作碼被保留做將來使用。除此之外，原始Java平臺開發商，昇陽微系統，額外保留了3個代碼永久不使用。

**與Java的關係**
一個Java程序員並不需要理解所有的Java字節碼。但是，就像IBM developerWorks週刊建議的那樣：“理解字節碼以及理解Java編譯器如何生成Java字節碼與學習彙編知識對於C/C++程序員有一樣的意義。”

**指令**
指令碼為一個字節，有256個可能的代碼值（28=256），因此一個字節的指令碼最多可能有256種不同的操作。其中，0x00、0xFE、0xCA、0xFF被指定保留。例如0xCA作為一個Java調試器的中斷指令而從未被語言使用。相似地，0xFE和0xFF也未被語言使用。
指令可以基本分為以下幾類：
*存儲指令 （例如：aload_0，istore）
*算術與邏輯指令 （例如: ladd，fcmpl）
*類型轉換指令 （例如：i2b，d2i）
*對象創建與操作指令 （例如：new，putfield）
*堆棧操作指令 （例如：swap，dup2）
*控制轉移指令 （例如：ifeq，goto）
*方法調用與返回指令 （例如：invokespecial，areturn)
除此之外，還有一些更特殊的指令，作為異常拋出或同步等作用。
大多數的指令有前綴和（或）後綴來表明其操作數的類型。如下表
例如，"iadd"指令將兩個整數相加；而"dadd"指令將兩個double浮點數相加。 
"const"、 "load"、 "store"等涉及存儲命令還會使用"_n"後綴，其中 "load"和"store"命令中的n可以為0到3之間的整數；而"const"命令中的n由類型指定。"const"指令把一個指定類型的值放入堆棧。例如"iconst_5"指令將一個整數5放入堆棧；而"dconst_1"將一個雙精度浮點數1放入堆棧。"aconst_null"指令是放入一個null進堆棧。而對於"load" "store"指令中的n，指定了變量表中的存儲位置。"aload_0"指令把在變量0中的對象（通常是"this"對象）放入堆棧，"istore_1"指令把棧頂的一個整數放入變量1。對於更高的變量，後綴將被去除，而這條指令將需要操作數。

**計算模型**

Java字節碼的計算模型是面向堆棧結構計算機的。例如，一個x86處理器的彙編代碼如下
mov eax, byte ebp-4
mov edx, byte ebp-8
add eax, edx
mov ecx, eax

這段代碼將兩個數值相加，並存入另一個地址。相似的反彙編字節碼如下
0 iload_1
1 iload_2
2 iadd
3 istore_3
在這裡，需要相加的兩個操作數被放入堆棧，而相加操作就在棧中進行，其結果也被放入堆棧。存儲指令之後把棧頂的數據放入一個變量地址。在每條指令前面的數字僅僅是表示這條指令到方法開始處的偏移值。這種堆棧結構也可以推廣到面向對象模型上。例如，有一個"getName"方法如下
Method java.lang.String getName()
0 aload_0       // "this"對象被存入變量0
1 getfield #5 
// 這個指令從棧頂取出一個對象，並從中搜索一個指定的域
// 並將相應的數據存入棧頂。
// 這個例子中，"name"域對應於該類中的第五個常量。

4 areturn  	 // 返回棧頂的對象作為函數的返回值


**例子**
考慮如下Java代碼
outer:
for (int i = 2; i 
假設上述代碼位於一個函數中，Java編譯器可能將代碼翻譯成下述的Java字節碼。
0:   iconst_2
1:   istore_1
2:   iload_1
3:   sipush  1000
6:   if_icmpge       44
9:   iconst_2
10:  istore_2
11:  iload_2
12:  iload_1
13:  if_icmpge       31
16:  iload_1
17:  iload_2
18:  irem
19:  ifne    25
22:  goto    38
25:  iinc    2, 1
28:  goto    11
31:  getstatic       #84; //Field java/lang/System.out:Ljava/io/PrintStream;
34:  iload_1
35:  invokevirtual   #85; //Method java/io/PrintStream.println:(I)V
38:  iinc    1, 1
41:  goto    2
44:  return

**基於Java字節碼的語言**

最常用的基於Java字節碼的語言就是開發出Java字節碼的Java語言。剛開始，只存在一個由昇陽微系統開發的一個編譯器javac。而現在Java字節碼規範已經可以得到，因此，第三方公司亦開發出支持Java字節碼的編譯器。例如：

*Jikes，編譯Java源代碼到Java字節碼（由IBM開發，用C++實現）
*Espresso，編譯Java源代碼到Java字節碼（僅支持Java 1.0）
*GCJ，GNU Compiler for Java，編譯Java代碼到Java字節碼；亦可以編譯到機器代碼。作為GNU Compiler Collection (GCC)的一部分提供。

有一些項目提供Java彙編器以便於直接用Java字節碼進行開發。主要的Java彙編器如下：
*Jasmin，讀取Java類的文字描述；用一種簡單的使用Java虛擬機指令的類彙編語法，輸出Java類文件 
*Jamaica， 一種為Java虛擬機編寫的宏彙編語言。其中，類與接口由Java語法定義，而其中的方法卻由Java字節碼定義。
還有其他的一些編譯器，對於其他語言生成Java字節碼，使其可以運行在Java虛擬機之上。

*Adobe ColdFusion
*JRuby和Jython， 兩種基於Ruby和Python的腳本語言
*Groovy, 一種基於Java的腳本語言
*Scala,一種類型安全的通用編程語言，支持面向對象編程和函數式編程
*JGNAT和AppleMagic,編譯Ada語言到Java字節碼
*Clojure, 一種函數式的通用編程語言，提供優秀的併發性。是一種LISP方言
*MIDletPascal
*JavaFX Script 由昇陽微系統公司開發的一種腳本語言，運行於Java虛擬機之上


**運行**

當前已經有很多種Java虛擬機產品，包括了自由軟件和商業軟件。如果在Java虛擬機之中執行Java字節碼並不理想，則可以使用一些工具例如GNU Compiler for Java將Java代碼或Java字節碼編譯成機器碼並由硬件直接運行。
而有一些處理器可以直接運行Java字節碼，這種處理器名為Java處理器。


**對動態語言的支持**

Java虛擬機對動態類型語言提供了一定的支持。但絕大多數的Java虛擬機指令集是基於靜態類型語言的。在靜態類型機制下，方法調用中的類型分析都是在編譯時執行的，而且缺乏一種機制在運行時確定一個類型已經確定相應的方法。

JSR292中，在Java虛擬機層次增加了一種支持動態類型的指令invokedynamic，以支持在動態類型檢測中的方法調用。 達芬奇機器則是一種支持這種動態類型調用的虛擬機。 而所有支持JSE 7的Java虛擬機都應支持invokedynamic操作碼。

**參考文獻 ==