JVM-スタックフレーム

3812 ワード

フレームは通常、データと一部の結果を格納するために使用され、通常はダイナミックリンク、メソッド戻り値、例外スケジューリングも実行されます.
メソッド呼び出しの同じ時点で、新しいフレームが作成され、メソッドの実行が完了すると(このメソッドが正常に完了しても予期せぬ完了しても)、フレームは破棄されます.スレッドがフレームを作成すると、Java仮想スタックにメモリ領域がフレームに割り当てられます.各フレームには、独自のローカル変数テーブル、オペランドスタック、現在のメソッド対応クラスのランタイムプールの参照があります.
コンパイル時に、ローカル変数テーブルのサイズ、オペランドスタック、およびフレームに関連付けられたメソッドバイトコードが決定される.さらに、フレームのデータ構造サイズは、JVMの実装に依存し、これらのデータ構造に必要なメモリ領域をメソッド呼び出しと同時に割り当てる.1つのメソッドのみが実行され、1つのフレームしかない場合、スレッドプロビジョニングに従っていつでもアクティブ化できます.このとき、このフレームを現在のフレームと呼び、このフレームに対応する方法を現在のメソッドと呼び、このメソッドが存在するクラスを現在のクラスと呼ぶ.ローカル変数テーブルとオペランドスタックの操作は、通常、現在のフレームを参照します.
メソッドが他のメソッドを呼び出したり、メソッドの実行が完了したりすると、メソッドのフレームが破棄されます.メソッドが制御権を新しいメソッドに移行すると、新しいメソッドが実行され、実行と同時に新しいフレームが生成され、新しいフレームが現在のフレームに切り替わります.メソッドの戻り値に対して、現在のフレームはフレーム対応メソッドの実行結果を返します.さらに、前のフレームに戻る可能性があります.現在のフレームが現在のフレームになると、前の現在のフレームは破棄されます.
==注:スレッドが作成するフレームはスレッドのプライベートスペースであり、他のスレッドは参照できません==
ローカル変数テーブル(local variables)
前述したように、各フレームに含まれる変数セットをその局所変数テーブルと呼び、1つのフレームの変数セットの長さはコンパイル時に決定され、クラスまたはインタフェースの方法に関連付けられたフレームもバイナリ形式として表現される.
単一のローカル変数の値のタイプは、基本タイプ、参照または戻りアドレスであってもよく、1対のローカル変数の値のタイプはlongまたはdoubleであってもよい.
ローカル変数テーブルはインデックスによってアドレスされ、最初のローカル変数のインデックスは0であり、すべてのインデックスは0からローカル変数テーブルの長さの減算の間でなければならない(array.length-1).
値のタイプがlongまたはdoubleである場合、ローカル変数ビットが2ビット占有され、このような値は小さなインデックスでしか処理できない.たとえば、doubleタイプの値がローカル変数テーブルに格納されているインデックスはnであり、実際にはその占有するローカル変数テーブルの位置はnとn+1の2つの位置であるが、インデックスがn+1のローカル変数はロードできず、n+1が格納できればn位置の変数は無効になる.
JVMはn位置の変数を要求しません.明らかに、longとdoubleのタイプの値は、ローカル変数テーブルで64ビットを整列させる必要はありません.JVMの実装では、64ビットを使用して格納するよりも、2つのローカル変数ビットを使用してこの値を格納する方が適しています.
メソッドが呼び出されると、JVMはローカル変数テーブルを使用してパラメータを渡します.クラスのメソッドが実行されると、すべてのパラメータは0で始まるローカル変数テーブルによって伝達されます.メソッド転送インスタンスでは、ローカル変数テーブルの最初のビットがメソッドインスタンスに対応するオブジェクト参照を格納するためによく使用されます(javaでは通常thisを指します).ローカル変数テーブルの2番目のビットからメソッドのパラメータを格納します.
オペランドスタック
前述したように、各フレームには後進先出のスタックがオペランドスタックと呼ばれ、現在のフレームの最大スタック深さはコンパイル時に決定され、方法に関連付けられたフレームもバイナリ形式で表現されている.
コンテキストの比較的明らかな位置では、現在のフレームのオペランドスタックを単純なオペランドスタックとして参照することがある.
フレームが作成されたばかりのときオペランドスタックは空であり、JVMは定数、ローカル変数、フィールドをオペランドスタックにロードする命令を提供し、他のJVM命令はオペランドスタックからオペランドを取得し、オペランド上で操作を行い、結果をオペランドスタックに返す.オペランドスタックは、メソッドに渡されるパラメータと受信メソッドの戻り値を準備するためにも使用できます.例:
iadd: iadd
Operation: Add int
Format: iadd
Forms: iadd = 96 (0x60)
Operand: ..., value1, value2 → 
Stack: ..., result

iaddメソッドの命令には2つのint値があり、前の命令の作用でメソッド要求のint値がオペランドスタックの最上位2ビットに追加され、その後、すべてのint値がオペランドスタックからポップアップされ、それらの合計もオペランドスタックに戻され、これらの計算操作はオペランドスタックにネストされてもよい.戻り値は計算に含めることができる.
オペランドスタック内の各エンティティは、longタイプまたはdoubleタイプを含むJVMの任意のタイプの値であってもよい.
オペランドスタックの値は、そのタイプで操作する必要がありますが、これは絶対的ではありません.たとえば、int値を2つ入力しますが、2つのlongまたは2つのfloatで入力し、isaddコマンドに入力して操作できます.実行時のデータ領域に少量のJVM命令が作用する場合、元の値として使用され、これらの命令はプライベート値を変更または中断するために使用できません.オペランドスタック上のこれらの操作ルールはclassファイルによって検証されます.
いつでも、オペランドスタックには関連する深さがあり、longタイプまたはdoubleタイプは深さの2つの単位を占有し、他のタイプは1つの単位を占有します.
ダイナミックリンク
前述したように、各フレームには、メソッドバイトコードの動的リンクをサポートするための現在のメソッドの実行時定数プールの参照が含まれている.メソッドは実行時にメソッドのclassファイル内のバイトコードを参照し、シンボルリファレンス(symbolic references)で変数にアクセスし、ダイナミックリンクはシンボルメソッドリファレンスを翻訳して具体的なメソッドリファレンスに入り、必要に応じてclassをロードしてまだ定義されていないシンボルを解析すると同時に、翻訳変数も、これらの変数の関連するランタイム位置をストレージ構造に見つける必要があります.
他のクラスのメソッドは、これらの最後にバインドされたメソッドと変数を中断することができる.
一般メソッド実行完了
1つのメソッドの実行に異常が発生しない場合、このメソッドの実行は正しく完了します.現在のメソッドが正常に実行されると、現在実行中のメソッドに値が返され、実行メソッドのいずれかがコマンド実行を返すと結果が返され、JVMは値のタイプに応じて戻りタイプが選択されます.
現在のフレームは、通常、ローカル変数テーブルとオペランドスタックを復元するなど、実行者の状態を復元するために使用され、実行者のプログラムカウンタは、メソッドに渡された命令をスキップして適切に追加します.現在実行中のメソッドが正常に終了すると、メソッドのフレームは、オペレーション結果を携えてオペランドスタックに戻ります.
予期せぬメソッドの実行が完了しました
メソッドの実行中に例外が発生してJVMが例外を放出したり、メソッド内で処理されていない例外が放出されたりした場合、メソッドが正常に終了しない場合、この方法は予期せぬメソッドの実行完了と呼ばれます.予期せぬ完了したメソッドの実行は、呼び出しに値を返さない.