Java仮想マシン

Java仮想マシン(JVM)のアーキテクチャ概要。ソースコードは一旦Javaバイトコードへとコンパイルされ、さらにインタプリタまたはJITコンパイラによりネイティブコードに変換されて実行される。Java APIとJVMの両者でJava実行環境(JRE)を構成する。

Java仮想マシン(ジャバかそうマシン、英語: Java virtual machineJava VM、JVM)は、Javaバイトコードとして定義された命令セットを実行するスタック型の仮想マシンAPIやいくつかのツールとセットでJava実行環境(JRE)としてリリースされている。この環境を移植することで、さまざまな環境でJavaのプログラムを実行することができる。

命令セット仕様

ニーモニック表

(12、C6 などの数値は16進法表記)

オペコード解説

スタック

  • bipushsipush - byte値、 short値をスタックに積む。
  • ldc - コンスタントプール内の4バイトの定数(int値、float値、java.lang.String)の内1バイト以内でエントリ番号を指定できるものをスタックに積む。
  • ldc_w - エントリ番号が1バイトでは足りないときに使う。
  • ldc2_w - コンスタントプール内の8バイトの定数(long値、double値)をスタックに積む。
  • iconst_m1iconst_0iconst_1iconst_2iconst_3iconst_4iconst_5 - int-1012345をスタックに積む。
  • lconst_0lconst_1 - long01をスタックに積む
  • fconst_0fconst_1fconst_2 - float012をスタックに積む
  • dconst_0dconst_1 - double01をスタックに積む。
  • aconst_null - スタックにnullを積む。
  • poppop2 - スタックから1、2ワード取り除く
  • dupdup2
  • dup_x1dup2_x1
  • dup_x2dup2_x2
  • swap

局所変数

  • iloadlloadfloaddloadaload - 局所変数からint値、 long値、 float値、 double値、 参照値を取り出してスタックに積む。
  • iload_0iload_1iload_2iload_3 - 0、1、2、3番目の局所変数からint値を取り出してスタックに積む。
  • lload_0lload_1lload_2lload_3 - 0、1、2、3番目の局所変数からlong値を取り出してスタックに積む。
  • fload_0fload_1fload_2fload_3 - 0、1、2、3番目の局所変数からfloat値を取り出してスタックに積む。
  • dload_0dload_1dload_2dload_3 - 0、1、2、3番目の局所変数からdouble値を取り出してスタックに積む。
  • aload_0aload_1aload_2aload_3 - 0、1、2、3番目の局所変数から参照値を取り出してスタックに積む。
  • istorelstorefstoredstoreastore - int値、 long値、 float値、 double値、 参照値を局所変数に格納。
  • istore_0istore_1istore_2istore_3 - int値を0、1、2、3番目の局所変数に格納する。
  • lstore_0lstore_1lstore_2lstore_3 - long値を0、1、2、3番目の局所変数に格納する。
  • fstore_0fstore_1fstore_2fstore_3 - float値を0、1、2、3番目の局所変数に格納する。
  • dstore_0dstore_1dstore_2dstore_3 - double値を0、1、2、3番目の局所変数に格納する。
  • astore_0astore_1astore_2astore_3 - 参照値を0、1、2、3番目の局所変数に格納する。

条件付きジャンプ

  • ifeqifnullifltifleifneifnonnullifgtifge - スタックの値が0、 null、 0未満、 0以下、 0以外、 null以外、 0より大きい、 0以上の場合に指定の番地に制御を移す。
  • if_icmpeqif_icmpneif_icmpltif_icmpgtif_icmpleif_icmpge - 2つのint値が等しい、等しくない、<、>、≦、≧の場合に指定の番地に制御を移す。
  • if_acmpeqif_acmpne - 2つの参照値が等しい、等しくない場合に指定の番地に制御を移す。

ジャンプ

  • gotogoto_w - それぞれ2バイト形式(符号付き16ビット)、4バイト形式(符号付き32ビット)でもつ相対番地をこの命令の番地に加えた番地に制御を移す(JavaにGO TO命令はなくても、このようにJava仮想マシンにgoto命令はある)。
  • jsrjsr_w - サブルーチンの先頭に制御を移す。gotoと違い、戻り番地を保存する。戻り番地の値はそれぞれ、制御を移す前の番地+3、制御を移す前の番地+5となる。
  • ret - サブルーチンから呼び出し元の戻り番地に制御を移す。番地が格納されている局所変数を指定する。
  • lookupswitch - switch文のcase式の値が不連続である場合に、値を探しながら飛び越し先を探し、飛び越しを行う。
  • tableswitch - switch文のcase式の値が連続である場合に、(キーがインデクス値、値が飛び越し先番地の)表を使って飛び越しを行う。(高速である)

メソッド呼び出し・復帰

  • invokevirtual - インスタンスメソッドを呼び出す。
  • invokespecial - インスタンス初期化メソッド、プライベートメソッド、スーパークラスのインスタンスメソッドを呼び出す。
  • invokestatic - クラスメソッドを呼び出す
  • invokeinterface - インターフェイスメソッドを呼び出す。
  • return - スタックに値を残さずに、呼び出し元の戻り番地に制御を移す。
  • ireturnlreturnfreturndreturnareturn - スタックに int値、 long値、 float値、 double値を残して、サブルーチンの呼び出し元の戻り番地に制御を移す。

型キャスト

  • checkcast - 参照値が指し示すインスタンスの型を確認。
  • instanceof - スタックから参照値をpopし、それが指定された型と同じであれば1を、異なれば0をスタックに積む。
  • i2li2fi2di2bi2ci2s - int値をlong値、 float値、 double値、 byte値、 char値、 short値に変換したものをスタックに残す。
  • l2il2fl2d - long値をint値に、 float値に、 double値に変換したものをスタックに残す。
  • f2if2lf2d - float値をint値に、 long値に、 double値に変換したものをスタックに残す。
  • d2id2ld2f - double値をint値に、 long値に、 float値に変換したものをスタックに残す。

比較演算

  • dcmpgdcmpl - double同士を比較し、1(大きい)、0(等しい)または-1(小さい)をスタックに残す。「-g」と「-l」の違いはオペランドがNaNだったときの扱いの差。(以下同様)
  • fcmpgfcmpl - float同士を比較する。
  • lcmp - long同士を比較する。

算術演算

  • iinc - intの値を、符号付き1バイト(値の範囲は-128~127)の直接記述した定数だけ(符号付きで32ビットに拡張したうえで)増減する。
  • iaddladdfadddadd - int値、 long値、 float値、 double値 で、スタックから取りだした第一の値に第二の値を加え、スタックに積む。
  • isublsubfsubdsub - int値、 long値、 float値、 double値 で、スタックから取りだした第一の値から第二の値を引き、スタックに積む。
  • imullmulfmuldmul - int値、 long値、 float値、 double値 で、スタックから取りだした第一の値に第二の値を掛け、スタックに積む。
  • idivldivfdivddiv - int値、 long値、 float値、 double値 で、スタックから取りだした第一の値を第二の値で割った商を、スタックに積む。
  • iremlremfremdrem - int値、 long値、 float値、 double値 で、スタックから取りだした第一の値を第二の値で割った余り(value1 - (value1 / value2) * value2)を、スタックに積む。
  • ineglnegfnegdneg - int値、 long値、 float値、 double値 で、スタックから取りだした値の符号を反転した値(たとえば入力が123なら-123、-123なら123、0なら0)(すなわち2の補数)をスタックに積む。

論理演算

  • ishllshl - int値、 long値 を指定ビットだけ左にシフトした値をスタックに残す。
  • ishrlshr - int値、 long値 を指定ビットだけ右に算術シフトした値をスタックに残す。負数はシフト後も負に維持される。
  • iushrlushr - int値、 long を指定ビットだけ右に論理シフトした値をスタックに残す。
  • iandland - int値、 long値 の2オペランドのAND(ビットごとの論理積)を求め、スタックに残す。
  • iorlor - int値、 long の2オペランドの OR(ビットごとの論理和)を求め、スタックに残す。
  • ixorlxor - int値、 long の2オペランドのXOR(ビットごとの排他的論理和)を求め、スタックに残す。

オブジェクト

  • new - インスタンスの生成
  • putfieldgetfield - メンバ変数への値の代入、値の取り出し
  • putstaticgetstatic - クラス変数への値の代入、値の取り出し

配列

  • ialoadlaloadfaloaddaloadaaloadbaloadcaloadsaload - 配列の指定された位置にあるint値、 long値、 float値、double値、参照値、byteまたはboolean値、char値、 short値をスタックに残す。
  • iastorelastorefastoredastoreaastorebastorecastoresastore - 配列にint値、 long値、 float値、 double値、 参照値、 byteまたはboolean値、 char値、 short値を格納する。
  • newarray - 数値や文字の配列を作成し、その参照値をスタックに残す。
  • anewarray - 参照値の配列を作成し、その参照値をスタックに残す。
  • multianewarray - 多次元配列を作成し、その参照値をスタックに残す。
  • arraylength - 配列の長さを求め、スタックに残す。

その他

  • athrow - java.lang.Throwableのインスタンスで例外やエラーを発生させる。
  • nop - 何もしない (no operation)。
  • breakpoint - デバッガがブレークポイントの実装に使える命令。
  • monitorenter - オブジェクトのモニタをロックする。既に他のスレッドにロックされていれば待たされる。
  • monitorexit - オブジェクトのモニタをアンロックする。他のスレッドのロック待ちは再度試行される。
  • wide - ロード/ストア系命令やretiincのインデックスを16ビットに拡張する。iincでは定数も16ビットに拡張する。

実装系

エンタープライズ用(デスクトップ用を包含)としては、オラクルIBMHPなどの各社から実装系がリリースされている。OS上でアプリケーションとして動作する形態が一般的である。

Windowsにも標準でJava仮想マシンが実装されていたが、マイクロソフトサン・マイクロシステムズとの契約に反して自社仕様の拡張機能を付加したため、Windows XP以降のOSではJavaの技術使用ライセンスを失った。

また、オープンソースコミュニティの手によってIKVM.NETという共通言語ランタイム上で動作するJava仮想マシンの実装も進められている。

変わった試みとしてGNU SmalltalkのVM上で構築されたJava仮想マシンが存在する。

picoJava, Jazelle などJava仮想マシンの命令がハードウェア実装されたプロセッサ、すなわちバイトコードを直接実行可能なプロセッサも存在する。

JITコンパイル

最初のJava仮想マシンの実装(JDK 1.0)はインタプリタ型であったため、動作速度が他のアプリケーションに比べて遅い場合があった。そのため、メソッドの実行直前(Just in Time)にバイトコードをCPUのネイティブコードにコンパイルして実行する形式(JITコンパイラ)を、ボーランドIBMなどがリリースした。サン・マイクロシステムズの実装もJDK 1.1からJITコンパイラを搭載した。

加えて、JDK 1.2から、サン・マイクロシステムズはHotSpotという高速化技術を導入した。HotSpotはJITコンパイラの一種だが、常にJITコンパイルを行うのではなく、実行回数が規定回数を超えたメソッド (Hotspot) のみをJITコンパイルする。これにより、JITコンパイルによる無駄なリソースの消費を防いだり、インタプリタ実行時のプロファイリング情報をJITコンパイル利用できる利点がある。HotSpotには用途別に、クライアントVM(コンパイルは高速だが生成されるネイティブコードが相対的にあまり最適化されない)と、サーバVM(コンパイルは低速だが生成されるネイティブコードが相対的により最適化される)がある。

スレッド

  • グリーンスレッド - pthread等、OSが提供するスレッドライブラリを直接使わずJavaで仮想的なスレッドを作り実行する形式。現在はあまり利用されていない。
  • ネイティブスレッド - OSが提供するスレッドライブラリとJavaスレッドが1x1で対応する形式。

ガベージコレクション

  • 世代別GC - ヒープを2つ以上の世代に分割し、それぞれに異なるアルゴリズム(およびデータ構造)を適用する方式。

GCアルゴリズム(データ構造)

  • コピーGC
  • Mark & Sweep
  • Mostly Concurrent Mark & Sweep
  • Mark & Compact

脚注

関連項目

外部リンク

Uses material from the Wikipedia article Java仮想マシン, released under the CC BY-SA 4.0 license.