Javaバイトコード
Javaバイトコードは、Java仮想マシンが実行する命令形式である。各バイトコードのオペコードは長さが1バイトであるが、引数を持つものもあるため、結果として複数バイトの命令となる。256個のオペコードの全てが使われているのではなく、51個が将来のために予約されている。その他について、Javaプラットフォームの開発元であるサン・マイクロシステムズは、3つのコードを永久に実装しないままにした。
Javaプログラマは、Javaバイトコードについて知ったり理解する必要は全くない。しかしながら、IBMのdeveloperWorksによると、「バイトコードを理解することと、どんなバイトコードがJavaコンパイラにより生成される可能性が高いかを理解することは、アセンブリ言語の知識がCやC++プログラマの助けになるのと同じように、Javaプログラマの助けになる」とされている。
各バイトは256個の値を持ちうるので、256個のオペコードが可能である。これらのうち、0x00から0xca、0xfe、0xffが割り当てられた値である。0xcaはデバッガ用のブレイクポイント命令として予約されており、この言語は利用しない。同様に、0xfeおよび0xffはこの言語により使用されず、仮想マシンの内部利用のために予約されている。
命令はいくつかの大まかな分類に分けられる:
ロードおよびストア(例 aload_0,istore)
算術および論理(例 ladd,fcmpl)
型変換(例 i2b,d2i)
オブジェクト作成および操作(new,putfield)
オペランドスタック管理(例 swap,dup2)
制御の移動(例 ifeq,goto)
メソッド呼び出しおよび復帰(例 invokespecial,areturn)
例外を投げたり同期を行うなどのように、多くのより専門化されたタスクのための命令もいくつか存在する。
多くの命令は、扱うオペランドの型を示す接頭辞や接尾辞を持つ。これらは以下の通り:
例えば、"iadd" は2つのintegerを加算し、"dadd" は2つのdoubleを加算する。"const"、"load"、そして "store" 命令は、"_n" という形式の接尾辞も取る。nは数字で、 "load" および "store" に対しては0から3までの値を取る。"const" に対してはnの最大値は型により違う。
"const" 命令はスタックに指定された型の値をプッシュする。例えば "iconst_5" は、integer 5をプッシュする。その一方、"dconst_1" はdouble 1をプッシュする。"null" をプッシュする "aconst_null" も存在する。"load" および "store" 命令用の nは、ロードやストアする変数テーブル内の場所を指定する。"aload_0" 命令はスタックに変数0であるオブジェクト(このオブジェクトは通常 "this" オブジェクト)をプッシュする。"istore_1" はスタックのトップにあるintegerを変数1にストアする。より大きい数の変数に対しては、この形式の接尾辞は削除し、演算子を使用する必要がある。
Java仮想マシン(の計算モデル)は、いわゆるスタックマシンである。例として、次のようなx86のコードを考える:
2つの値を加算して別の場所にその結果をコピーする。類似のJavaバイトコードは以下のようになる:
ここで、加算される2つの値はスタックに積まれ、加算命令によりスタックから値が回収され、加算され、そして結果がスタックに戻される。それからストア命令がスタックのトップの値を変数の場所へ移動する。命令の前にある数は、メソッドの最初から各命令のオフセットを単に表しているだけである。このスタック指向モデルは、この言語のオブジェクト指向の側面にも及ぶ。例えば、"getName()" というメソッドの呼出しは以下のようになる:
以下のJavaコードを考えよう:
上記がメソッド内に置かれていると仮定すると、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; //フィールド java/lang/System.out:Ljava/io/PrintStream;
34:  iload_1
35:  invokevirtual   #85; //メソッド java/io/PrintStream.println:(I)V
38:  iinc    1, 1
41:  goto    2
44:  return
Javaバイトコードを生成する、Java仮想マシンをターゲットとした最も一般的な言語はJavaである。元々は、サン・マイクロシステムズからのjavacという、たった1つしかコンパイラしか存在しなかった。javacはJavaソースコードをJavaバイトコードへとコンパイルする。しかし現在ではJavaバイトコードに対するすべての仕様が利用可能であるため、他のパーティーがJavaバイトコードを生成するコンパイラを供給している。他のコンパイラの例は以下の通り:
Jikes - JavaからJavaバイトコードへとコンパイルする(IBMにより開発され、C++で実装されている)
Espresso - JavaからJavaバイトコードへとコンパイルする(Java 1.0のみ)
GCJ - Java用GNUコンパイラで、JavaからJavaバイトコードへとコンパイルする。これはネイティブな機械語にコンパイルすることもでき、GNUコンパイラコレクション (GCC) の一部として利用可能である。
いくつかのプロジェクトは、手動でJavaバイトコードを書く事を可能とするためのJavaアセンブラを供給する。アセンブリコードは、Java仮想マシンをターゲットとするコンパイラによるものを例として、マシンによっても生成される。有名なJavaアセンブラは以下の通り:
Jasmin (ソフトウェア) - Java仮想マシン命令セットを利用するシンプルなアセンブリライクな構文規則で記述されたJavaクラス用のテキスト記述を得て、Javaクラスファイルを生成する。
Jamaica - Java仮想マシン用のマクロアセンブリ言語。Java構文規則はクラスやインタフェースのために利用される。メソッド本体はバイトコード命令を用いて指定される。
その他にも、異なるプログラミング言語用のJava仮想マシンをターゲットとするために開発されたコンパイラがある。それらは以下の通り:
Groovy - Javaを基盤としたスクリプト言語
Scala - オブジェクト指向および関数型プログラミングをサポートする、型セーフな多目的プログラミング言語
JGNATおよびAdaMagic - AdaからJavaバイトコードへのコンパイルをする
JavaFX ScriptコードもJavaバイトコードにコンパイルされる
JavaバイトコードはJava仮想マシン内で実行されるように設計されている。今日ではフリーおよび商用ともに様々な仮想マシンが存在する。
実行するJava仮想マシン内のJavaバイトコードが望ましくない場合、開発者はGCJのようなツールを使用することで、JavaソースコードやJavaバイトコードを直接ネイティブコートにコンパイルすることもできる。いくつかのプロセッサはJavaバイトコードをネイティブに実行することができる。そのようなプロセッサはJavaプロセッサとして知られている。
Java仮想マシン (JVM) のJVM命令セットおよびメソッド呼び出し機構は、メソッド呼び出しのシグネチャをコンパイル時に型チェックする、静的型付けベースと言える。
JSR 292(Java™ プラットフォーム上の動的型付き言語のサポート)により、(静的型検査ベースのinvokevirtualの代わりとして)動的型検査ベースの新規のinvokedynamic命令が追加された。Da Vinci Machineは、動的言語サポート向けのJVM拡張をホストするプロトタイプ仮想マシン実装である。Java SE 7をサポートする全てのJVMにもinvokedynamic命令が含まれる。