「Java仮想マシンの詳細な理解」リーディングノート第7章仮想マシンクラスのロードメカニズム

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

クラス読み込みのタイミング

ここに画像の説明を挿入します
分析は、初期化後に実行できます。これは、Java言語のランタイムバインディング機能(動的バインディングまたは遅延バインディングとも呼ばれます)をサポートするためです。次の6つのケースのみがあり、オブジェクトをすぐに初期化する必要があります(もちろん、ロード検証の準備は完了しています)
1:新しいgetstatic putstaticまたはinvokestaticの4つのバイトコード命令に遭遇したとき、型が初期化されていない場合、次に、初期化フェーズを最初にトリガーする必要があります。これらの4つの命令を生成できる一般的なJavaコードは次のとおりです
。1。newキーワードを使用してオブジェクトをインスタンス化する場合
2.静的変数のタイプの読み取りまたは設定(* finalで変更された静的変数はコンパイル段階で定数プールに格納されます)したがって、最終変数を除く)
3。型の静的メソッドを呼び出す場合。
2:型へのリフレクティブ呼び出し。型が初期化されていない場合は、すぐに初期化する必要があります
。3:クラスが初期化されるときに、親クラスがまだ初期化されていない場合は、親クラスの初期化をトリガーする必要があります。最初
4:仮想マシンの起動時に、実行するメインクラス(mainメソッド)を指定する必要があります仮想マシンは最初にこのクラスを初期化します
5:JDK7の新しく追加された動的言語サポートを使用する場合、最終分析の場合MethodHandleインスタンスの結果はREF_getStaticREF-putStatic REF_invokeStatic REF_newInvokeSpecial 4つのタイプメソッドハンドルのメソッドハンドル、およびこのメソッドハンドルに対応するクラスが初期化されていないため、最初に初期化する必要があります(QAQが何であるかは徐々にわかりません) )について話している
6:インターフェースはJDK8で追加された新しいデフォルトのメソッドを定義する(デフォルトキーワードの変更)このインタフェースの実装クラスが初期化されている場合、インターフェイスは、その前に初期化する必要があります。
上記の6つのメソッドはアクティブ参照と呼ばれ、他のすべての参照メソッドは初期化をトリガーせず、パッシブアプリケーションと呼ばれます。
パッシブリファレンスの例

public class Test1 {
    
    
    static {
    
    
        System.out.println("父类初始化");
    }
    public static  final int finalStaticVal = 123;
    public static  int staticVal = 123;
    public static void fun(){
    
    
        System.out.println("父类静态方法");
    }
}
public class Test2 extends Test1{
    
    
    static {
    
    
        System.out.println("子类初始化");
    }
}

テスト1

public class Test3 {
    
    
    public static void main(String[] args) {
    
    
        System.out.println(Test2.staticVal);
    }
}

ここに画像の説明を挿入します
サブクラスを介して親クラスの静的変数を呼び出しても、サブクラスは初期化されません。

テスト2

public class Test3 {
    
    
    public static void main(String[] args) {
    
    
        System.out.println(Test2.finalStaticVal);
    }
}

ここに画像の説明を挿入します
親クラスの最後の静的変数が呼び出され、親クラスとサブクラスは初期化されません

テスト3

public class Test3 {
    
    
    public static void main(String[] args) {
    
    
        Test2.fun();
    }
}

ここに画像の説明を挿入します
親クラスの静的メソッドを呼び出します。子クラスは初期化されません

クラスの読み込みプロセス

負荷

仮想マシンは、ロードフェーズ中に次の3つのことを完了する必要があり
ます。1。クラスの完全修飾名を使用してこのクラスを定義するバイナリバイトストリームを取得します
。2。バイトストリームで表される静的ストレージ構造をのランタイムに変換します。メソッドデータ構造
3.メモリ内にこのクラスを表すClassオブジェクトを生成します。これは、このクラスのさまざまなデータにアクセスするための最も簡単な方法です。

仮想マシンは、クラスのバイナリバイトストリームを.classファイルから取得する必要があることを指定していません。正確には、どこでどのように取得するかを指定していません。たとえば
、これは非常に一般的なZIP圧縮パッケージから取得でき、将来的にはjar EAR WAR形式の基礎になり
ます。ネットワークから取得します。
実行時の計算と生成。このシナリオでは、動的プロキシテクノロジ
は、他のファイルから(JSP)に変換されます。-「クラスファイル」
は、データベースなどから読み取られます

ロードフェーズは、仮想マシンの組み込みクラスローダーを使用するか、ユーザー定義のクラスローダーを使用して完了することができます。
ロードフェーズと接続フェーズのアクションの一部(バイトコードファイル形式の検証アクションの一部など)はインターリーブされます。
ロードフェーズが完了すると、仮想マシンの外部のバイナリバイトストリームは、仮想マシンによって設定された形式に従ってメソッド領域に格納されます。

検証

検証中の接続フェーズの最初のステップ。このフェーズの目的は、クラスファイルのバイトストリームに含まれる情報が「Java仮想マシン仕様」のすべての制約要件を満たしていることを確認することです。この情報がコードとして実行された後、仮想マシン自体のセキュリティを危険にさらさないことを確認してください
.Java言語自体は比較的安全なコンパイル言語です(Javaコードは境界を越えて配列にアクセスできず、オブジェクトに値を自由に割り当てることはできません、等。)。ただし、これらの操作はバイトコードレベルで実装できます。クラスファイルはさまざまな方法で生成できます(たとえば、手動で入力します)。仮想マシンが入力バイトストリームをチェックせず、完全に信頼している場合、間違ったバイトコードまたは悪意を持って試行されたバイトコードがロードされる可能性があります。このフローにより、システムは攻撃について話します。検証フェーズは、大きく次の4つのステップに分かれています。

1.ファイル形式の検証

検証フェーズの主な目的は、入力バイトストリームを正しく解析してメソッド領域に格納できること、および形式がJavaタイプ情報を記述する要件を満たしていることを確認することです。
例:マジックナンバー0XCAFEBABEで始まる
かどうか、メジャーバージョン番号とマイナーバージョン番号が現在のJava仮想マシンの許容範囲内にある
かどうか。定数プール内の定数が、定数を
指す定数タイプでサポートされていないかどうか。。存在しない定数を指す定数があるかどうか、またはタイプに準拠していない定数などがあるかどうか

ファイル形式で検証されたバイトストリームのみが、ストレージ用のJava仮想マシンメモリのメソッド領域に入ることが許可されているため、次の3つの段階はすべてメソッド領域のストレージ構造に基づいており、これ以上の単語はありません読み取りまたは操作。スロットリング

2.メタデータの検証

第2段階は、
このクラスに親クラス
があるかどうか、親クラスを継承できる
かどうか、非抽象クラスが親クラスのインターフェイスと抽象メソッドを実装するかどうかなど、バイトコードによって記述される情報のセマンティック分析です。
など。
このステージの主な目的は、クラスのメタデータ情報に対してセマンティック検証を実行して、「Java言語仕様」の定義と矛盾するメタデータ情報がないことを確認することです。

3.バイトコードの検証

第3段階は、検証プロセスの最も複雑な段階です。主な目的は、データフローと制御フローを分析することです。プログラムのセマンティクスが合法かつ論理的であることを確認してください。第2段階でメタデータ情報のデータ型を確認した後、この段階でクラスのメソッド本体を確認および分析して、仮想マシンに害を及ぼす動作がないことを確認します。
たとえば
、バウチャー内のジャンプ命令は
メソッド本体外のバイトコード命令にジャンプせず、メソッド本体内の型変換が有効であることを保証します。

4.シンボル参照の検証

最後のフェーズの検証動作は、仮想マシンがシンボリック参照を直接参照に変換するときに発生します。この変換アクションは、接続の3番目のフェーズである解析フェーズで完了します。シンボル参照の検証は、クラス自体(定数プール内のさまざまなシンボル参照)以外のすべての種類の情報の一致する行の検証と見なすことができます。一般に、クラスが欠落しているか、クラスの一部へのアクセスが禁止されているかどうかです。リソース外部クラス、メソッドフィールドなど。
たとえば
、シンボル参照の文字列で記述された完全修飾名が対応する
クラスを見つけることができるかどうか。指定されたクラスに、メソッドフィールド記述子と単純名および
クラスフィールドメソッドで記述されたメソッドを満たすルームサテライトはありますか。フィールドシンボル参照内?現在のクラスからアクセスできます

シンボル参照の検証は、主に解析フェーズの正常な実行を保証するためのものです。

検証フェーズは重要ですが、必須ではありません。

準備ができました

準備段階では、これらの静的変数に初期値を割り当てます。非最終静的変数の場合は、対応するタイプの値0が割り当てられます。最終タイプの場合は、コードに入力された値に直接割り当てられます。

構文解析

解析段階は、Java仮想マシンが定数プール内のシンボル参照を直接参照に置き換えるプロセスです。
シンボル参照:シンボル参照は、参照されるターゲットを説明するためのシンボルのセットです。シンボルは、任意の形式のリテラルにすることができます。ターゲットを使用するときに明確に配置できる限り、
直接参照できます。直接参照はポインターです。ターゲットを直接指すことができます。相対オフセット、またはターゲットに間接的に配置できるハンドル。直接参照がある場合、おばあちゃんが使用するターゲットは、仮想マシンのメモリにすでに存在している必要があります。

初期化

初期化フェーズは、クラスロードプロセスの最後のステップです。準備フェーズでは、静的変数に値0が割り当てられます。初期化フェーズでは、クラス変数およびその他のリソースは、によって指定された主観的な計画に従って初期化されます。プログラムコーディングによるプログラマー。初期化フェーズは()メソッドを実行するプロセスであるとも言えます。このメソッドは、コンパイル時に自動的に生成されます。彼はクラス内のすべての静的変数を収集して、アクションと静的ステートメントブロックをコピーします。

クラスローダー

クラスローダーはクラスのロードアクションを実装するためにのみ使用されますが、Javaプログラムでのその役割はロード段階をはるかに超えています。どのクラスでも、それをロードするクラスローダーとクラス自体は、他のJava仮想マシンでの一意性を共同で確立する必要があります。たとえば、同じクラスローダーによってロードされた同じクラスのみが等しくなります。2つのクラスが同じclssファイルからのものであっても、それらのクラスローダーが異なる場合、それらは待ちたくありません。

親の委任メカニズム

ここに画像の説明を挿入します
親委任モデルの作業プロセスは次のとおりです。クラスローダーがクラスロードの要求を受信した場合、最初に自分でクラスをロードしようとはしませんが、各レベルで要求を親クラスローダーに委任して完了させます。すべてのクラスローダーの場合。したがって、すべてのクラス読み込み要求は、最終的には最上位のスタートアップクラスローダーに送信される必要があります。親クラスローダーが読み込み要求を完了できない(必要なクラスが検索範囲に見つからない)と報告した場合にのみ、その中にあります。自分の権利。自分でロードを終了しようとします。
利点:Javaのクラスには、クラスローダーとともに優先度のある階層関係があります。たとえば、java.lang.Objectはrt.jarに存在し、これらのタイプをロードするクラスローダーに関係なく、最終的にはモデルの最上位にあるスタートアップクラスローダーに委任されてロードされます。したがって、Objectクラスが使用されます。クラスローダー環境は同じクラスであることが保証されています。親委任メカニズムが使用されていない場合、各クラスローダーはそれを単独でロードします。ユーザーがjava.lang.Objectというクラスを作成し、それをClassPathディレクトリに配置すると、システムには未使用のObjectクラスが複数存在します。

おすすめ

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