「Java仮想マシンの詳細な理解」リーディングノート第8章仮想マシンのバイトコード実行エンジン

クリックして、「Java仮想マシンの詳細な理解」を表示します。

ランタイムスタックフレーム構造

Java仮想マシンは、最も基本的な実行ユニットとしてメソッドを採用しています。スタックフレームは、仮想マシンをサポートするメソッド呼び出しとメソッド実行の背後にあるデータ構造であり、仮想マシンスタックのランタイムデータ領域とスタック要素でもあります。スタックフレームには、メソッドのローカル変数テーブル、オペランドスタック、動的リンクメソッドのリターンアドレス、およびPCカウンタとその他の情報が格納されます。

ローカル変数テーブル

ローカル変数テーブルは、メソッドパラメータとメソッド内で定義されたローカル変数を格納するために使用される一連の変数値の格納スペースです。Javaプログラムがクラスファイルにコンパイルされると、メソッドに割り当てる必要のあるローカル変数テーブルの最大容量は、メソッドのCode属性のmax_localsデータ項目で決定されます。
ローカルリンクリストの容量は可変スロットに基づいていますが、doubleとLongの両方が1つのスロットを占有します(2つのスロットを占有します)。スロットは再利用できます。

オペランドスタック

オペランドスタックの深さもコンパイル時に決定されます。オペランドを操作する前に、オペランドをオペランドスタックに入れる必要があります。たとえば、算術演算を実行するときは、最初にオペランドをオペランドスタックの最上位にプッシュしてから、算術命令を呼び出して続行します。

動的リンク

各スタックフレームには、ランタイム定数プールでスタックフレームが属するメソッドへの参照が含まれています。この参照は、メソッド呼び出し中の動的接続をサポートするために保持されます。クラスファイルの定数プールには多数のシンボル参照があります。バイトコードのメソッド呼び出し命令は、定数プールのメソッドを指すシンボル参照をパラメータとして受け取ります。これらのシンボル参照の一部は、クラスのロード段階または最初に使用されるときに直接参照に変換されます。この変換は静的解決と呼ばれます。他の部分は実行されるたびに直接参照に変換され、この部分はダイナミックリンクと呼ばれます。

メソッドの戻りアドレス

メソッドの実行を開始するとき、このメソッドを起動する方法は2つあります。1つはreturnステートメントです。もう1つは、例外をスローすることです。いずれにせよ、メソッドが終了した後、元のメソッドが呼び出された場所に戻る必要があります。正常に終了した場合は、PCカウンタがスタックフレームに格納されている可能性があり、このプログラムカウンタをメソッドのリターンアドレスとして使用できます。
異常終了の場合は、例外テーブルに従ってリターンアドレスを決定する必要があります。

追加情報

仮想マシンは、仕様に記載されていない情報をスタックフレームに追加できます。たとえば、デバッグとパフォーマンス収集に関連する情報。情報のこの部分は、特定の仮想マシンの実装によって異なります。

メソッド呼び出し

メソッドの呼び出しは、メソッド内のコードが実行されることを意味するのではなく、呼び出されるメソッドのバージョンを判別することを意味します。

構文解析

プログラムが実行される前にメソッドに決定可能な呼び出しバージョンがある場合、クラスのロードの解析フェーズで、メソッドのシンボリック参照が直接参照に直接変換されます。このタイプのメソッドの呼び出しは、解決と呼ばれます。
Javaでは、実行時に不変であるというコンパイラーの要件を満たすメソッドには、静的メソッド、finalメソッド、プライベートメソッド、親メソッド、およびコンストラクターメソッドが含まれます。これらのメソッドは非仮想メソッドと呼ばれ、他のメソッドは仮想メソッドと呼ばれます。
解析呼び出しは静的プロセスである必要があり、コンパイル中に完全に決定できます。クラス読み込みの解析段階では、デザインのすべてのシンボル参照が明示的な直接参照に変換されます。

ディスパッチ

ディスパッチ呼び出しプロセスは、書き換えやオーバーロードなど、ポリモーフィズムの最も基本的な兆候のいくつかを明らかにします

静的ディスパッチ


public class Main1 {
    
    
    static class Human {
    
    
    }
    static class Father extends Human{
    
    
    }
    static class Son extends Human{
    
    
    }
    public void who(Human human){
    
    
        System.out.println("i am human");
    }
    public void who(Father father){
    
    
        System.out.println("i am father");
    }
    public void who(Son son){
    
    
        System.out.println("i am son");
    }

    public static void main(String[] args) {
    
    
        Main1 main1 = new Main1();
        Human father = new Father();
        Human son = new Son();
        main1.who(son);
        main1.who(father);
    }
}

ここに画像の説明を挿入します
例として
Humanfather = new Father();
ステートメントを取り上げます
。Humanは静的型または外観型と呼ばれ、Fatherは実際型または実行時型と呼ばれます。
静的タイプと実際のタイプの両方がプログラム内で変更される場合があります。、違いは、静的型は使用された場合にのみ変更され、変数自体の静的型は適応されないことです。
そして、最終的な静的型はコンパイル時にわかります。実際の型変更の結果は、実行時にのみ決定できます。
オーバーロードする場合、実際の型ではなく、パラメーターの静的型によって決定されます。
メソッドの実行バージョンを決定するために静的型に依存するすべてのディスパッチアクションは、静的ディスパッチと呼ばれます。静的ディスパッチの最も古典的なアプリケーションシナリオは、オーバーロードです。静的ディスパッチは、コンパイルフェーズ中に発生します。

ダイナミックディスパッチ

動的ディスパッチと書き換えは密接に関連しています。ポリモーフィズムのある方法ですが、フィールドにはポリモーフィズムがありません。

public class Main1 {
    
    
    static class Father {
    
    
        int age;
        public Father(){
    
    
            age = 40;
            show();
        }
        public void show(){
    
    
            System.out.println("father is "+age+" age");
        }
    }
    static class Son extends Father{
    
    
        int age;
        public Son(){
    
    
            age = 10;
            show();
        }
        public void show(){
    
    
            System.out.println("son is "+age+" age");
        }
    }

    public static void main(String[] args) {
    
    
        Father son = new Son();
        son.show();
    }
}

ここに画像の説明を挿入します
動的ディスパッチでは、最初にパラメーターの実際のタイプを判別してから、実際のタイプに基づいて実行するメソッドのバージョンを見つける必要があります。

解釈と実行

ここに画像の説明を挿入します
ホットスポーツ仮想マシンは、スタックの解釈と実行に基づいています。

おすすめ

転載: blog.csdn.net/qq_30033509/article/details/115031487