Java仮想マシン

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

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

命令セット仕様

ニーモニック表

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

  -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -A -B -C -D -E -F
0- 00
nop
01
aconst_null
02
iconst_m1
03
iconst_0
04
iconst_1
05
iconst_2
06
iconst_3
07
iconst_4
08
iconst_5
09
lconst_0
0A
lconst_1
0B
fconst_0
0C
fconst_1
0D
fconst_2
0E
dconst_0
0F
dconst_1
1- 10
bipush
11
sipush
12
ldc
13
ldc_w
14
ldc2_w
15
iload
16
lload
17
fload
18
dload
19
aload
1A
iload_0
1B
iload_1
1C
iload_2
1D
iload_3
1E
lload_0
1F
lload_1
2- 20
lload_2
21
lload_3
22
fload_0
23
fload_1
24
fload_2
25
fload_3
26
dload_0
27
dload_1
28
dload_2
29
dload_3
2A
aload_0
2B
aload_1
2C
aload_2
2D
aload_3
2E
iaload
2F
laload
3- 30
faload
31
daload
32
aaload
33
baload
34
caload
35
saload
36
istore
37
lstore
38
fstore
39
dstore
3A
astore
3B
istore_0
3C
istore_1
3D
istore_2
3E
istore_3
3F
lstore_0
4- 40
lstore_1
41
lstore_2
42
lstore_3
43
fstore_0
44
fstore_1
45
fstore_2
46
fstore_3
47
dstore_0
48
dstore_1
49
dstore_2
4A
dstore_3
4B
astore_0
4C
astore_1
4D
astore_2
4E
astore_3
4F
iastore
5- 50
lastore
51
fastore
52
dastore
53
aastore
54
bastore
55
castore
56
sastore
57
pop
58
pop2
59
dup
5A
dup_x1
5B
dup_x2
5C
dup2
5D
dup2_x1
5E
dup2_x2
5F
swap
6- 60
iadd
61
ladd
62
fadd
63
dadd
64
isub
65
lsub
66
fsub
67
dsub
68
imul
69
lmul
6A
fmul
6B
dmul
6C
idiv
6D
ldiv
6E
fdiv
6F
ddiv
7- 70
irem
71
lrem
72
frem
73
drem
74
ineg
75
lneg
76
fneg
77
dneg
78
ishl
79
lshl
7A
ishr
7B
lshr
7C
iushr
7D
lushr
7E
iand
7F
land
8- 80
ior
81
lor
82
ixor
83
lxor
84
iinc
85
i2l
86
i2f
87
i2d
88
l2i
89
l2f
8A
l2d
8B
f2i
8C
f2l
8D
f2d
8E
d2i
8F
d2l
9- 90
d2f
91
i2b
92
i2c
93
i2s
94
lcmp
95
fcmpl
96
fcmpg
97
dcmpl
98
dcmpg
99
ifeq
9A
ifne
9B
iflt
9C
ifge
9D
ifgt
9E
ifle
9F
if_icmpeq
A- A0
if_icmpne
A1
if_icmplt
A2
if_icmpge
A3
if_icmpgt
A4
if_icmple
A5
if_acmpeq
A6
if_acmpne
A7
goto
A8
jsr
A9
ret
AA
tableswitch
AB
lookupswitch
AC
ireturn
AD
lreturn
AE
freturn
AF
dreturn
B- B0
areturn
B1
return
B2
getstatic
B3
putstatic
B4
getfield
B5
putfield
B6
invokevirtual
B7
invokespecial
B8
invokestatic
B9
invokeinterface
BA
invokedynamic
BB
new
BC
newarray
BD
anewarray
BE
arraylength
BF
athrow
C- C0
checkcast
C1
instanceof
C2
monitorenter
C3
monitorexit
C4
wide
C5
multianewarray
C6
ifnull
C7
ifnonnull
C8
goto_w
C9
jsr_w
CA
breakpoint
CB
未定義
CC
未定義
CD
未定義
CE
未定義
CF
未定義
D- D0
未定義
D1
未定義
D2
未定義
D3
未定義
D4
未定義
D5
未定義
D6
未定義
D7
未定義
D8
未定義
D9
未定義
DA
未定義
DB
未定義
DC
未定義
DD
未定義
DE
未定義
DF
未定義
E- E0
未定義
E1
未定義
E2
未定義
E3
未定義
E4
未定義
E5
未定義
E6
未定義
E7
未定義
E8
未定義
E9
未定義
EA
未定義
EB
未定義
EC
未定義
ED
未定義
EE
未定義
EF
未定義
F- F0
未定義
F1
未定義
F2
未定義
F3
未定義
F4
未定義
F5
未定義
F6
未定義
F7
未定義
F8
未定義
F9
未定義
FA
未定義
FB
未定義
FC
未定義
FD
未定義
FE
impdep1
FF
impdep2

オペコード解説

スタック

  • 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仮想マシンが存在する。[1]

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

脚注

  1. ^ smalltalk/packages/java at master · gnu-smalltalk/smalltalk” (英語). GitHub. 2015年2月18日閲覧。

関連項目

外部リンク