Javaバイトコード

出典: フリー百科事典『ウィキペディア(Wikipedia)』
移動: 案内検索

Javaバイトコードは、Java仮想マシンが実行する命令形式である。各バイトコードオペコードは長さが1バイトであるが、引数を持つものもあるため、結果として複数バイトの命令となる。256個のオペコードの全てが使われているのではなく、51個が将来のために予約されている。その他について、Javaプラットフォームの開発元であるサン・マイクロシステムズは、3つのコードを永久に実装しないままにした。[1]

Javaとの関係[編集]

Javaプログラマは、Javaバイトコードについて全く知ったり理解する必要はない。しかしながら、IBMのdeveloperWorksによると、「バイトコードを理解することと、Javaコンパイラにより生成されるバイトコードが何であるかを理解することは、アセンブリ言語の知識がCC++プログラマの助けになるのと同じように、Javaプログラマの助けになる」とされている。[2] [3]

命令[編集]

各バイトは潜在的な256個の値を持つが、256個の使用可能なオペコードを持つ。これらのうち、0x00から0xca、0xfe、そして0xffは割り当てられた値である。0xcaはデバッガ用のブレイクポイント命令として予約されており、言語は利用しない。同様に、0xfeおよび0xffは言語により使用されず、仮想マシンの内部利用のために予約されている。

命令は幅広い数に分類される:

  • ロードおよびストア(例 aload_0,istore)
  • 算術および論理(例 ladd,fcmpl)
  • 型変換(例 i2b,d2i)
  • オブジェクト作成および操作(new,putfield)
  • オペランドスタック管理(例 swap,dup2)
  • 移動管理(例 ifeq,goto)
  • メソッド呼び出しおよび復帰(例 invokespecial,areturn)

例外を投げたり同期を行うなどのように、多くのより専門化されたタスクのための命令もいくつか存在する。

多くの命令は、動作するオペランドの型を示す接頭辞や接尾辞を持つ。これらは以下の通り:

接頭辞 / 接尾辞 オペランド型
i integer
l long
s short
b byte
c character
f float
d double
z boolean
a reference

例えば、"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" はスタックのトップに変数1となるintegerをストアする。より高度な数を用いる変数のため、接尾辞はドロップされ、演算子を使用する必要がある。

計算模型[編集]

Javaバイトコードの計算模型は、スタック指向言語英語版の計算模型である。例えば、x86プロセッサ用のアセンブリコードが以下のようであるとしよう:

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

このコードは2つの値を加算して異なる場所にその結果を移動する。類似の逆アセンブルされたバイトコードは以下のようになる:

0 iload_1
1 iload_2
2 iadd
3 istore_3

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

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

[編集]

以下の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という、たった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クラスファイルを生成する。[4]
  • Jamaica - Java仮想マシン用のマクロアセンブリ言語。Java構文規則はクラスやインタフェースのために利用される。メソッド本体はバイトコード命令を用いて指定される。[5]

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

実行[編集]

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

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

動的言語のサポート[編集]

Java仮想マシン (JVM) は動的型付け言語のためのサポートを提供する。現存するJVM命令セットの大半は、メソッド呼び出しがそれらのシグネチャをコンパイル時に型チェックするという意味で、このランタイムへの決意を延期するため、または代わりのアプローチによってメソッドディスパッチを選ぶためのメカニズムなしで、静的型付けである。[6]

JSR 292(Java™ プラットフォーム上の動的型付け言語のサポート[7]により、(現存する静的な型検査であるinvokevirtualの代わりとして)動的型検査に依存するメソッド呼び出しを与えるため、JVMレベルで新規のinvokedynamic命令が追加された。Da Vinci Machineは、動的言語のサポート向けのJVM拡張をホストするプロトタイプ仮想マシン実装である。Java SE 7をサポートする全てのJVMにもinvokedynamicオペコードが含まれる。

関連項目[編集]

脚注[編集]

外部リンク[編集]