この記事は読書ノートです
1.基本的な概念
Java仮想マシンは、最も基本的な実行単位としてメソッドを使用します。「スタックフレーム」は、仮想マシンのメソッド呼び出しとメソッド実行をサポートするために使用されるデータ構造です。仮想マシンスタックのスタック要素。
**基本構成:**ローカル変数テーブル、オペランドスタック、ダイナミックリンク、メソッドの戻りアドレス、その他の追加情報。
Javaプログラムのソースコードをコンパイルするときに、スタックフレームで必要なローカル変数テーブルの量、オペランドスタックを分析および計算し、メソッドテーブルのCodeプロパティに書き込む必要がある深さ
Javaプログラムの観点から見ると、呼び出しスタックのすべてのメソッドは、同時に同じ状態で同じスレッドにあります。実行エンジンの場合、アクティブスレッドでは、スタックの最上位のメソッドのみが実行され、スタックの最上位のスタックフレームのみが有効です。これは** "現在のスタックフレーム" **( CurrentStack Frame)、このスタックフレームに関連付けられたメソッドは「現在のメソッド」と呼ばれます。
springbootソースコードに一部があります:
この部分は、SpringApplication.classの構築メソッド内にあり、メインメソッドエントリを取得するクラスです。その操作は、現在のスタックフレームを取得することです。現在のスタックフレームの
基本的なプロセスは、ランタイム例外を作成し、スタック配列を取得してトラバースすることです。 StackTraceElement配列、メソッド名が「mian」かどうかを判断し、そうであれば、Class.forName()メソッドでClassオブジェクトを作成します。
コードは非常に単純ですが、SpringBootを使用すると、非常にわかりやすくなります。Javaの例外処理を比較して、StackTraceの使用についてさらに学習しましょう。
Stacktrace(スタックトレース)は、非常に便利なデバッグツールです。プログラムで例外が発生したり、手動で例外をスローしたりすると、エラー箇所が表示され、エラーの階層関係が発生します。
2.ローカル変数テーブル
メソッド内で定義されたメソッドパラメータとローカル変数を格納するためのストレージスペース。
JavaプログラムがClassファイルにコンパイルされるとき、メソッドが割り当てる必要があるローカル変数テーブルの最大容量は、メソッドのCode属性のmax_localsデータ項目で決定されます。
32ビットのboolean、byte、char、short、int、float、reference [illustration]およびreturnAddressの場合、1スロットで十分です。
64ビットのデータ型の場合、Java仮想マシンは2つの連続する可変スロットスペースをハイビットに整列した方法で割り当てます。
Java仮想マシンは、インデックスの配置を通じてローカル変数テーブルを使用します**インデックス値の範囲は、0からローカル変数テーブルの変数スロットの最大数までです。** 32ビットデータタイプの変数にアクセスしている場合、インデックスNはN番目の変数スロットの使用を表します。64ビットデータタイプの変数にアクセスしている場合、N番目とN + 1の両方の変数が使用されますスロット。
メソッドが呼び出されると、Java仮想マシンはローカル変数テーブルを使用して、パラメーター値のパラメーター変数リストへの転送プロセス、つまり、実際のパラメーターの仮パラメーターへの転送を完了します。
ローカル変数テーブルの変数スロットは再利用可能です。
再利用の例:
int a = 0がない場合、gcはトリガーされず、使用可能になった後でgcがトリガーされます。
理由:
ローカル変数テーブルの変数スロットにまだ情報があるかどうかプレースホルダー配列オブジェクト参照。最初の変更では、コードはプレースホルダーのスコープを離れましたが、その後、ローカル変数テーブルへの読み取りまたは書き込み操作は発生していませんが、プレースホルダーによって元々占有されていた変数スロットは他の変数によって再利用されていません、そのため、GC Rootsの一部としてのローカル変数テーブルは、その関連付けを維持します。
3.オペランドスタック
オペランドスタック(オペランドスタック)はしばしば操作スタックと呼ばれ、後入れ先出し(後入れ先出し、LIFO)スタックです。
オペランドスタックの各要素は、longおよびdoubleを含む任意のJavaデータ型にすることができます。32ビットデータタイプが占有するスタック容量は1で、64ビットデータタイプが占有するスタック容量は2です。
4.動的接続
各スタックフレームには、ランタイム定数プール[図]でスタックフレームが属するメソッドへの参照が含まれています。この参照は、メソッド呼び出し中の動的接続をサポートするために保持されます。
5.メソッドの戻りアドレス
メソッドを終了するには、2つの方法しかありません:1.正常に戻り、値を上位の呼び出し元に返す2.例外をスローし、例外が適切に処理されない。
一般的に、メソッドが正常に終了すると、呼び出し側メソッドのPCカウンターの値を戻り値として使用できます。アドレス、スタックカウンターは、このカウンター値を保存する可能性があります。メソッドが異常終了した場合、戻りアドレスは例外ハンドラテーブルによって決定され、通常、この情報の部分はスタックフレームに保存されません。
終了時に可能な操作は、ローカル変数テーブルと上位メソッドのオペランドスタックを復元し、戻り値(ある場合)を呼び出し元のスタックフレームのオペランドスタックにプッシュし、PCカウンターの値を調整してメソッド呼び出しを指すようにします。指導後の指導等
6.追加情報
デバッグとパフォーマンスの収集に関連する情報。情報のこの部分は、特定の仮想マシンの実装に完全に依存します。
通常、動的接続、メソッドの戻りアドレス、およびその他の追加情報はすべて、スタックフレーム情報と呼ばれるすべてのグループにまとめられます
举例:i++, ++i
スタックベースの解釈実行とも呼ばれます
int a = 10;
int b = a++ + ++a + a--;
System.out.println(a);
System.out.println(b);
画像はステーションb jvmのダークホース復号からのものです
7.メソッド呼び出し
Classファイルのコンパイルプロセスには、従来のプログラミング言語のコンパイルの接続手順は含まれていません。Classファイルに保存されているすべてのメソッド呼び出しは、シンボリック参照のみであり、実際のランタイムメモリレイアウト(つまり、直接参照)。
Java仮想マシンは、バイトコード命令を呼び出すための次の5つのメソッド、つまり、invokestaticをサポートしています。静的メソッドを呼び出すために使用されます。
・特別な呼び出し。インスタンスコンストラクター<init>()メソッド、プライベートメソッド、および親クラスのメソッドを呼び出すために使用されます。
・仮想を呼び出します。すべての仮想メソッドを呼び出すために使用されます。
・インターフェイスを呼び出します。インターフェイスメソッドを呼び出すために使用され、インターフェイスを実装するオブジェクトは実行時に決定されます。
・Invokedynamic。呼び出しポイント修飾子によって参照されるメソッドは、実行時に動的に解決され、メソッドが実行されます。最初の4つの呼び出し命令のディスパッチロジックはJava仮想マシンで固定されており、invokedynamic命令のディスパッチロジックは、ユーザーが設定したブートメソッドによって決定されます。
invokestaticおよびinvokespecial命令によって呼び出すことができるメソッドである限り、解析フェーズ中に呼び出すバージョンのみを決定できます。この条件を満たすJava言語には、静的メソッド、プライベートメソッド、インスタンスコンストラクター、および親メソッドに加え、4つのメソッドがあります。finalによって変更されたメソッド(invokevirtual命令を使用して呼び出されます)ですが、これらの5つのメソッド呼び出しは、クラスが読み込まれるときに、シンボル参照をメソッドへの直接参照に解決します。これらのメソッドはまとめて「非仮想メソッド」と呼ばれ、他のメソッドは「仮想メソッド」と呼ばれます。
派遣
Man man = new Women();
Manは静的型、Womenは動的型、
したがって、メソッドの実行は、左側の静的タイプまたは右側の動的タイプに応じて、静的ディスパッチと動的ディスパッチに分けられます。
**静的ディスパッチの最も一般的なアプリケーションパフォーマンスは、メソッドのオーバーロードです。**静的ディスパッチはコンパイル段階で発生するため、静的ディスパッチアクションは実際には仮想マシンによって実行されていないと判断されます。そのため、一部の資料では、「ディスパッチ」ではなく「解析」として分類しています。
動的ディスパッチの実装プロセスは、Java言語のpolymorphism-Overrideの別の重要な兆候と密接に関連しています。
動的に型付けされた言語
動的型付け言語の主な機能は、型チェックの主なプロセスが、コンパイル時ではなく実行時に
「変数には型がないが変数値には型がある」ことです。
以下は、Java内の動的メソッドです
java.lang.invokeパッケージは、println()をAで実行します
リフレクションのように見えますが、
MethodHandleは、メソッドとエフェクトの使用において、リフレクションと多くの類似点があります。
ReflectionおよびMethodHandleメカニズムは基本的にメソッド呼び出しをシミュレートしますが、** ReflectionはJavaコードレベルでメソッド呼び出しをシミュレートし、MethodHandleはバイトコードレベルでメソッド呼び出しをシミュレートします。** MethodHandles.Lookupの3つのメソッドfindStatic()、findVirtual()、findSpecial()は、これらのバイトコード命令invokestatic、invokevirtual(およびinvokeinterface)、invokespecialの実行許可検証動作に対応します。これらの低レベルの詳細は、リフレクションAPIを使用する際に考慮する必要はありません。
** Reflectionのjava.lang.reflect.Methodオブジェクトは、MethodHandleメカニズムのjava.lang.invoke.MethodHandleオブジェクトに含まれる情報をはるかに上回っています。**前者はJava側のメソッドの包括的なイメージであり、メソッドのシグニチャー、記述子、メソッド属性テーブルのさまざまな属性のJava側の表現、および実行許可などのランタイム情報が含まれます。後者には、メソッドを実行するための関連情報のみが含まれています。開発者の口語表現では、Reflectionは重量級であり、MethodHandleは軽量です。
・MethodHandleはバイトコードのメソッド命令呼び出しのシミュレーションであるため、理論的には、この領域で仮想マシンによって行われたさまざまな最適化(メソッドのインライン化など)は、MethodHandleの同様のアイデアによってサポートされます(ただし、現在実装されています)。それはまだ改善中です)、そしてリフレクションを介してメソッドを呼び出すことによってさまざまな呼び出しポイント最適化手段を直接実装することはほとんど不可能です。
祖先メソッドを呼び出します:
MethodHandles.LookupクラスはUnsafeクラスの取得に似ていますが、このクラスは新しいものではありません