Javaバイトコード

**Javaバイトコード**とは、Javaプログラムの実行時にJava仮想マシンが解釈または実行するバイトコードである。Javaコンパイラはソースコードをこの命令列に変換し、クラスファイルとして保存する。

## Javaとの関係
Javaプログラマは、Javaバイトコードについて知ったり理解したりする必要は全くない。しかしながら、IBMのdeveloperWorksに投稿された記事では、「バイトコードを理解することと、どんなバイトコードがJavaコンパイラにより生成される可能性が高いかを理解することは、アセンブラ（アセンブリ言語）の知識がCやC++プログラマの助けになるのと同じように、Javaプログラマの助けになる」と述べられている。

## 命令

各バイトは256個の値を持ちうるので、256個のオペコードが可能である。これらのうち、0x00から0xcaまでの範囲と、0xfe、0xffが割り当てられた値である。0xcaはデバッガ用のブレークポイント命令として予約されており、Java言語は使用しない。同様に、0xfeおよび0xffは実装固有の機能に対するバックドアまたはトラップを提供することを意図して予約されており、Java言語は使用しない。

命令はいくつかの大まかな分類に分けられる:
* ロードおよびストア（例: 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のコードを考える:

```asm
mov eax, byte [ebp-4]
 mov edx, byte [ebp-8]
 add eax, edx
 mov ecx, eax
```

2つの値を加算して別の場所にその結果をコピーする。類似のJavaバイトコードは以下のようになる:

```java
0 iload_1
 1 iload_2
 2 iadd
 3 istore_3
```

ここで、加算される2つの値はスタックに積まれ、加算命令によりスタックから値が回収され、加算され、そして結果がスタックに戻される。それからストア命令がスタックのトップの値を変数の場所へ移動する。命令の前にある数は、メソッドの最初から各命令のオフセットを単に表しているだけである。このスタック指向モデルは、この言語のオブジェクト指向の側面にも及ぶ。例えば、"getName()" というメソッドの呼び出しは以下のようになる:

```java
Method java.lang.String getName()
 0 aload_0       // "this" オブジェクトが変数テーブルの場所0にストアされる。
 1 getfield #5 <Field java.lang.String name>
                 // この命令はスタックのトップからオブジェクトをポップし、
                 // そのオブジェクトから指定されたフィールドを取得し、
                 // そしてスタックにそのフィールドをプッシュする。
                 // この例では、"name" フィールドがこのクラスの定数プールの5番目に対応する。
 4 areturn       // メソッドからスタックのトップのオブジェクトを返す。
```


## 例
以下のJavaコードを考えよう:

```java
outer:
  for (int i = 2; i < 1000; i++) {
      for (int j = 2; j < i; j++) {
          if (i % j == 0)
              continue outer;
      }
      System.out.println(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; //フィールド 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というコンパイラのみが唯一の実装として存在していた。javacはJavaソースコードをJavaバイトコードへとコンパイルする。しかしではJavaバイトコードに対するすべての仕様が利用可能であるため、他のパーティーがJavaバイトコードを生成するコンパイラを提供している。他のコンパイラの例は以下の通り:
*  - JavaからJavaバイトコードへとコンパイルする（IBMにより開発され、C++で実装されている）
* Espresso - JavaからJavaバイトコードへとコンパイルする（Java 1.0のみ）
* GCJ - Java用GNUコンパイラで、JavaからJavaバイトコードへとコンパイルする。これはネイティブな機械語にコンパイルすることもでき、GNUコンパイラコレクション (GCC) の一部として利用可能である。

いくつかのプロジェクトは、手動でJavaバイトコードを書くことを可能とするためのJavaアセンブラを提供する。アセンブリコードは、Java仮想マシンをターゲットとするコンパイラによるものを例として、マシンによっても生成される。有名なJavaアセンブラは以下の通り:

*  - Java仮想マシン命令セットを利用するシンプルなアセンブリライクな構文規則で記述されたJavaクラス用のテキスト記述を得て、Javaクラスファイルを生成する。
* Jamaica - Java仮想マシン用のマクロアセンブリ言語。Java構文規則はクラスやインタフェースのために利用される。メソッド本体はバイトコード命令を用いて指定される。

その他にも、Java仮想マシンをターゲットとする、Javaとは異なるパラダイムを持つプログラミング言語用に開発されたコンパイラがある。それらは以下の通り:

* ColdFusion 
* JRubyおよびJython - RubyとPythonを基盤とした2つのスクリプト言語
* Groovy - Javaを基盤とした汎用スクリプト言語
* Scala - オブジェクト指向および関数型プログラミングをサポートする、型セーフな多目的プログラミング言語
* JGNATおよび - AdaからJavaバイトコードへのコンパイルをする
* CからJavaバイトコードへのコンパイラ
* Clojure
* コードもJavaバイトコードにコンパイルされる
* Kotlin

## 実行

JavaバイトコードはJava仮想マシン内で実行されるように設計されている。今日ではフリーおよび商用ともに様々な仮想マシンが存在する。

実行するJava仮想マシン内のJavaバイトコードが望ましくない場合、開発者はGCJのようなツールを使用することで、JavaソースコードやJavaバイトコードを直接ネイティブコードにコンパイルすることもできる。いくつかのプロセッサはJavaバイトコードをネイティブに実行することができる。そのようなプロセッサは「Javaプロセッサ」として知られている。

## 動的言語のサポート

Java仮想マシン (JVM) のJVM命令セットおよびメソッド呼び出し機構は、メソッド呼び出しのシグネチャをコンパイル時に型チェックする、静的型付けベースと言える。

JSR 292（Java&trade; プラットフォーム上の動的型付き言語のサポート）により、（静的型検査ベースの`invokevirtual`の代わりとして）動的型検査ベースの新規の`invokedynamic`命令が追加された。Da Vinci Machineは、動的言語サポート向けのJVM拡張をホストするプロトタイプ仮想マシン実装である。Java SE 7をサポートする全てのJVMにも`invokedynamic`命令が含まれる。

## 関連項目
* Javaクラスファイル
* Java仮想マシン
* 共通中間言語 (CIL)

## 脚注

## 外部リンク
* Java仮想マシン仕様 
* Bytecode Visualizer  - バイトコードビューワおよびデバッガ（Eclipseプラグイン）