Java 仮想マシン命令は、特定の意味を表すバイト長のオペレーション コード (Opcode) と、その後に続くオペレーション パラメータを表す 0 個以上のオペランドで構成されます。仮想マシン内の多くの命令にはオペランドは含まれず、オペコードのみが含まれます。例外が無視されれば、JVM インタープリターは 1 つのコードだけで効果的に機能します。
次のようにコードをコピーします。
する{
PC レジスタを自動的に計算し、PC レジスタの場所からオペコードを取得します。
if(オペランドが存在する) オペランドを取り出します。
オペコードで定義された操作を実行します。
}while (次のループを処理)
オペランドの数と長さはオペコードによって異なります。オペランドの長さが 1 バイトを超える場合、オペランドはビッグエンディアン順 (上位の最初のバイトコード) で格納され、その値は (byte1<<8) | となる必要があります。バイト2。
「tableswitch」命令と「lookupswitch」命令を除いて、バイトコード命令ストリームはシングルバイトで整列されており、それらのオペランドは特殊であり、整列を実現するには対応するギャップを予約する必要があります。
Java 仮想マシンのオペコードの長さを 1 バイトに制限し、コンパイルされたコードのパラメーターの長さの調整を放棄することで、JVM 実装に一定のパフォーマンス コストがかかる場合でも、短く無駄のないコンパイルされたコードを取得できます。オペコードの長さは 1 バイトのみであるため、命令セットの数が制限され、データが適切にアライメントされているとは想定されていません。つまり、データが 1 バイトを超えると、特定のデータ構造を元のデータから再構築する必要があります。一部のバイトのパフォーマンスが失われます。
データ型と Java 仮想マシン
JVM の命令セットでは、ほとんどの命令にその操作に対応するデータ型情報が含まれています。たとえば、iload 命令はローカル変数テーブルから int 型データをオペランド スタックにロードしますが、fload 命令は float 型データをロードします。
データ型に関連するほとんどのバイトコード命令のオペコード ニーモニックには、次のことを示す特殊文字があります。i は int 型を表し、l は long を表し、s は short を表し、b は byte を表し、c は char を表し、f は float を表し、d は double を表し、 aは参照を表します。必要に応じて、サポートされていない型をサポートされている型に変換するために使用できる個別のディレクティブがあります。
命令のロードとストア
ロードおよびストア命令は、スタック フレームのローカル変数テーブルおよびオペランド スタックとの間でデータを転送するために使用されます。
1) ローカル変数をオペランド スタックにロードする命令には、iload、iload_<n>、lload、lload_<n>、float、fload_<n>、dload、dload_<n>、aload、aload_<n> が含まれます。
2) オペランド スタックからローカル変数に値を格納する命令: istore,istore_<n>,lstore,lstore_<n>,fstore,fstore_<n>,dstore,dstore_<n>,astore,astore_<n>
3) 定数をオペランド スタックにロードする命令: bipush、sipush、ldc、ldc_w、ldc2_w、aconst_null、iconst_ml、iconst_<i>、lconst_<l>、fconst_<f>、dconst_<d>
4) ローカル変数テーブルのアクセスインデックス命令:ワイド
山括弧で終わる一部の命令は、iload_0、iload_1 などを表す iload_<i> など、命令のグループを表します。これらの命令のグループは、1 つのオペランドを持つ一般的な命令です。
操作説明
算術命令は、2 つのオペランド スタックの値に対して特定の演算を実行し、その結果を演算スタックの最上部に格納するために使用されます。
1) 加算命令: iadd、ladd、fadd、dadd
2) 減算命令: isub、lsub、fsub、dsub
3) 乗算命令: imul、lmul、fmul、dmul
4) 除算命令: idiv、ldiv、fdiv、ddiv
5) 残りの命令: irem、lrem、frem、drem
6) 負の命令: ineg、length、fneg、dneg
7) 変位命令: ishl、ishr、iushr、lshl、lshr、lusr
8) ビット単位の OR 命令: ior、lor
9) ビット単位の AND 命令: iand、land
10) ビットごとの XOR 命令: ixor、lxor
11) ローカル変数インクリメント命令:iinc
12) 比較命令: dcmpg、dcmpl、fcmpg、fcmpl、lcmp
Java 仮想マシンでは、整数データのオーバーフローについては明確に規定されていませんが、整数データを処理する場合、除数と剰余命令のみが仮想マシンに除数が 0 の場合に例外をスローさせると規定しています。
命令のロードとストア
ロードおよびストア命令は、スタック フレームおよびオペランド スタックのローカル変数テーブルとの間でデータを転送するために使用されます。
1) ローカル変数をオペランド スタックにロードする命令には、iload、iload_<n>、lload、lload_<n>、float、fload_<n>、dload、dload_<n>、aload、aload_<n> が含まれます。
2) オペランド スタックからローカル変数に値を格納する命令: istore,istore_<n>,lstore,lstore_<n>,fstore,fstore_<n>,dstore,dstore_<n>,astore,astore_<n>
3) 定数をオペランド スタックにロードする命令: bipush、sipush、ldc、ldc_w、ldc2_w、aconst_null、iconst_ml、iconst_<i>、lconst_<l>、fconst_<f>、dconst_<d>
4) ローカル変数テーブルのアクセスインデックス命令:ワイド
山括弧で終わる一部の命令は、iload_0、iload_1 などを表す iload_<i> など、命令のグループを表します。これらの命令のグループは、1 つのオペランドを持つ一般的な命令です。
操作説明
算術命令は、2 つのオペランド スタックの値に対して特定の演算を実行し、その結果を演算スタックの最上部に格納するために使用されます。
1) 加算命令: iadd、ladd、fadd、dadd
2) 減算命令: isub、lsub、fsub、dsub
3) 乗算命令: imul、lmul、fmul、dmul
4) 除算命令: idiv、ldiv、fdiv、ddiv
5) 残りの命令: irem、lrem、frem、drem
6) 負の命令: ineg、length、fneg、dneg
7) 変位命令: ishl、ishr、iushr、lshl、lshr、lusr
8) ビット単位の OR 命令: ior、lor
9) ビット単位の AND 命令: iand、land
10) ビットごとの XOR 命令: ixor、lxor
11) ローカル変数インクリメント命令:iinc
12) 比較命令: dcmpg、dcmpl、fcmpg、fcmpl、lcmp
Java 仮想マシンでは、整数データのオーバーフローについては明確に規定されていませんが、整数データを処理する場合、除数と剰余命令のみが仮想マシンに除数が 0 の場合に例外をスローさせると規定しています。
型変換命令
型変換命令は、2 つの Java 仮想マシンの数値型を相互に変換します。これらの操作は通常、ユーザー コードで明示的な型変換操作を実装するために使用されます。
JVM は拡張型変換 (狭い範囲の型変換から広い範囲の型変換へ) をサポートします。
1) int型からlong、float、double型へ
2) ロングタイプからフロート、ダブルタイプ
3) float 型から double 型へ
ナロー型変換命令: i2b、i2c、i2s、l2i、f2i、f2l、d2l、および d2f。ナロー型変換では、変換結果の符号や桁が異なる場合があります。変換プロセスにより、数値の精度が失われる可能性があります。たとえば、int または long 型が整数型 T に変換される場合、変換プロセスでは、最下位 N バイトの予期しない内容のみが破棄されます (N は型 T のデータ型の長さです)。
オブジェクトの作成と操作
クラス インスタンスと配列は両方ともオブジェクトですが、Java 仮想マシンはクラス インスタンスと配列の作成と操作に異なるバイトコード命令を使用します。
1) インスタンスの作成指示: new
2) 配列の作成手順: newarray、anewarray、multianewarray
3) アクセスフィールド命令: getfield、putfield、getstatic、putstatic
4) 配列要素をオペランド スタック命令にロードします: baload、caload、saload、iaload、laload、faload、daload、aaload
5) オペランド スタックの値を配列要素に格納し、bastore、castore、castore、sastore、iastore、fastore、dastore、aastore を実行します。
6) 配列長命令を取得します: arraylength
7) インスタンスタイプのチェック命令:instanceof、checkcast
オペランドスタック管理命令
オペランドスタックを直接操作する命令:pop、pop2、dup、dup2、dup_x1、dup2_x1、dup_x2、dup2_x2、swap
コントロール転送命令
JVM が制御転送命令ではなく、指定された命令に続く命令から条件付きまたは無条件でプログラムの実行を継続できるようにします。制御転送命令には次のものが含まれます。
1) 条件分岐: ifeq、iflt、ifle、ifne、ifgt、ifge、ifnull、ifnotnull、if_cmpeq、if_icmpne、if_imlt、if_icmpgt など。
2) 複合条件分岐: tableswitch、lookupswitch
3) 無条件分岐: goto,goto_w,jsr,jsr_w,ret
JVM には、int 型と参照型の条件分岐比較操作を処理するための特別な命令セットがあり、エンティティ値が null かどうかを明確に示すために、null 値を検出するための特別な命令があります。 boolean 型と byte 型、char 型と short 型の条件分岐の比較演算はすべて int 型の比較命令で完結し、long、float、double の条件分岐の比較演算は対応する型の比較演算命令で実行され、演算が行われます。命令は整数値をオペランド スタックに追加し、次に int 型の条件付き比較演算を実行して分岐ジャンプ全体を完了します。すべてのタイプの比較は、最終的に int タイプの比較演算に変換されます。
メソッドの呼び出しと戻りの命令
invokevirtual命令:オブジェクトのインスタンスメソッドを呼び出し、オブジェクトの実際の型に応じてディスパッチします(仮想マシンディスパッチ)。
invokeinterface 命令: インターフェイス メソッドを呼び出し、実行時にこのインターフェイス メソッドを実装するオブジェクトを検索し、呼び出す適切なメソッドを見つけます。
invokespecial: インスタンス初期化メソッド、プライベート メソッド、親クラス メソッドなど、特別な処理を必要とするインスタンス メソッドを呼び出します。
invokestatic: クラスメソッドの呼び出し(静的)
メソッドの戻り命令は戻り値の種類によって区別され、ireturn (戻り値は boolean、byte、char、short、int)、lreturn、freturn、drturn、areturn が含まれます。もう 1 つの return は void メソッド、インスタンス初期化メソッド用です。 、class、インターフェースのクラス初期化iメソッドを使用します。
同期
JVM は、メソッド レベルの同期とメソッド内の一連の命令の同期をサポートしており、どちらもモニターを通じて実装されます。
メソッドレベルの同期は暗黙的であり、バイトコード命令を通じて制御する必要はありません。メソッド呼び出しと戻り操作で実装されます。仮想マシンは、メソッド定数プール内のメソッド標準構造体の ACC_SYNCHRONIZED フラグによって、同期メソッドであるかどうかを識別します。メソッドが呼び出されるとき、呼び出し命令はフラグが設定されているかどうかを確認し、設定されている場合、実行スレッドはモニターを保持してからメソッドを実行し、最後にメソッドが完了するとモニターを解放します。
一連の命令セットを同期します。通常、同期ブロックによってマークされます。JVM 命令セットには、同期セマンティクスをサポートする Monitorenter および Monitorexit があります。
構造化ロックとは、メソッド呼び出し中の各モニターの終了が前のモニターのエントリと一致する場合です。 JVM は、次の 2 つのルールに従って構造化ロックが確実に確立されるようにします (T はスレッドを表し、M はモニターを表します)。
1) メソッドの実行中に T が M を保持する回数は、メソッドの完了時に T が M を解放する回数と等しくなければなりません。
2) いかなるときも、T が M を保持する回数よりも多く T が M を解放する状況は決してありません。