ホームページ  >  記事  >  Java  >  JVM クラスロードメカニズムの詳細な紹介 (コード例)

JVM クラスロードメカニズムの詳細な紹介 (コード例)

不言
不言オリジナル
2018-09-25 15:14:542149ブラウズ

この記事では、JVM クラスの読み込みメカニズムについて詳しく説明します (コード例)。必要な方は参考にしていただければ幸いです。

1. 概要

Java は、C/C などの従来のコンパイル言語とは異なり、PHP などの動的スクリプト言語とも異なります。 Java はセミコンパイル言語であると言えます。作成したクラスは、最初に .class ファイルにコンパイルされます。この .class はバイナリ バイト ストリームです。このクラスが使用されると、このクラスに対応する .class ファイルがメモリにロードされます。この .class のコンテンツは、Jvm クラス ロード メカニズムを通じてメモリにロードされます。

仮想マシンは、クラスを記述するデータをクラス ファイルからメモリにロードし、データを検証し、変換、解析、および初期化して、最終的に仮想マシンが直接使用できる Java 型を形成します。これは仮想マシンのロードメカニズムのクラスです。

2. クラスロードのさまざまな手順

ロード

ロード時の「クラスロード」プロセスの最初のステップ, 読み込みプロセス中に、仮想マシンは次の 3 つのことを完了する必要があります。

  1. #クラスの完全修飾名を使用して、このクラスを定義するバイナリ バイト ストリームを取得します。

  2. このバイト ストリームで表される静的ストレージ構造をメソッド領域の実行時データ構造に変換します。

  3. このクラスを表す java.lang.Class オブジェクトをメモリ内に生成します。これは、メソッド領域内のこのクラスのさまざまなデータへのアクセス エントリとして機能します。

読み込みフェーズは、システムによって提供されるブート クラス ローダーまたはユーザー定義のクラス ローダーを使用して完了できることに言及する価値があります。 ただし、これは比較的無料です。これは配列の場合には当てはまりません。 配列クラス自体はクラスのロードを通じて作成されるのではなく、Java 仮想マシンによって直接作成されます。ただし、データが保存される要素タイプはクラス ローダーによって作成される必要があります。

ローディングフェーズと次のフェーズの接続部分はインターリーブされますが、ローディングフェーズと接続フェーズの開始時間は固定シーケンスを維持します。

検証

検証は接続フェーズの最初のステップです。このフェーズの目的は、クラス ファイルのバイト ストリームに含まれる情報が正しいことを確認することです。現在の仮想マシンの要件と組み合わせることで、仮想マシン自体のセキュリティが危険にさらされることはありません。範囲外の配列やオブジェクトのランダムな変換などの操作はコンパイラによって拒否されますが、.class ファイルは必ずしも Java ソース コードからコンパイルする必要はなく、他の方法からバイナリ ストリームを生成できます。 .class ファイルを確認する必要があります。

検証フェーズの重要性は、実行パフォーマンスの観点から、この段階が厳格であるかどうかによって、Java 仮想マシンが悪意のあるコード攻撃に耐えられるかどうかに直接影響します。負荷は、仮想マシンのクラス ローディング システムのかなりの部分を占めます。

全体として、検証フェーズは、ファイル形式検証、メタデータ検証、バイトコード検証、シンボル参照検証の 4 つの検証アクションの部分に大別できます。

  • シンボル検証: 主な目的は、入力バイト ストリームが正しく解析されてメソッド領域に格納できること、および形式が Java 型情報を記述するための要件を満たしていることを確認することです。 。この部分はバイナリ ストリーム検証に基づいており、バイナリ ストリームはメモリにロードされ、その後の検証はメモリ内で実行されます。

  • メタデータ検証: この検証では主にクラスのメタデータ情報の意味検証を実行し、Java 言語仕様に準拠していないメタデータ情報がないことを確認します。

  • バイトコード検証: この部分は検証段階の最も複雑な段階であり、主な目的は、データ フローと制御フローの分析を通じてプログラムが合法的かつ論理的であることを判断することです。

  • シンボル参照の検証: シンボル参照は、解析アクションを正常に実行できるように、仮想マシンがシンボル参照を直接参照に変換するときに発生します。

準備

準備フェーズは、形式的なクラス変数 (静的変数) とその初期値にメモリを割り当てるフェーズです。クラス変数が設定され、使用されるメモリはメソッド領域に割り当てられます。この時点ではインスタンス変数ではなく、クラス変数 (静的変数) のみが割り当てられることに注意してください。

通常、クラス変数の初期値は、データ型のデフォルト値 (int 型の場合は 0) を指します。ただし、クラス変数がfinalで変更された場合は状況が異なります。その場合は、指定された値が直接代入されます。

解析

解析フェーズは、仮想マシンが定数プール内のシンボル参照を直接参照に置き換えるプロセスです。ここでは、シンボリック参照とは何か、直接参照とは何かについて説明します。

シンボリック参照: シンボリック参照では、使用時にターゲットを明確に特定できる限り、シンボルのセットを使用して参照先のターゲットを記述します。

直接参照: 直接参照は、ターゲットへのポインター、相対オフセット、またはターゲットを間接的に見つけることができるハンドルにすることができます。

解析アクションは主に、クラスまたはインターフェイス、フィールド、クラス メソッド、インターフェイス メソッド、メソッド タイプ、メソッド ハンドル、および呼び出しサイト修飾子の 7 種類のシンボル参照に対して実行されます。

初期化

クラスの初期化フェーズは、クラスのロード プロセスの最後のステップです。前のクラスのロード プロセスでは、ロード フェーズ中にユーザー アプリケーションがカスタム クラス ローダーを介して参加できる点を除き、残りのアクションは完全に支配され、制御されます。仮想マシン。初期化フェーズでは、クラスに定義された Java コードが実際に実行を開始します。

準備フェーズでは、システムが必要とする初期値が変数に割り当てられ、初期化フェーズでは、プログラマがプログラムを通じて作成した計画領域に従って、クラス変数やその他のリソースが初期化されます。

3. 興味深いコード セグメント

public class StaticTest
 {

     public static void main(String[] args)
     {
         staticFunction();
     }
  
     static StaticTest st = new StaticTest();
  
     static
     {
         System.out.println("1");
     }
  
     {
         System.out.println("2");
     }
  
     StaticTest()
     {
         System.out.println("3");
         System.out.println("a="+a+",b="+b);
     }
  
     public static void staticFunction(){
         System.out.println("4");
     }
  
     int a=110;
     static int b =112;
 }

このコードの結果は何でしょうか?

答えは次のとおりです:

2
3
a=110,b=0
1
4

これはなぜでしょうか。次のことを考えてみるとよいでしょう。

このコードを理解するには、Java のクラス読み込みメカニズムだけでなく、静的コード ブロックと静的メンバー変数の初期化シーケンスもコード シーケンスに関連していることを理解する必要があります。

クラスのロードのプロセスは、ロード -> 接続 (検証、準備、解析) -> 初期化です。

1. 準備フェーズでは、クラス変数にデフォルト値が設定されるため、ケース 1: st=null、b=0、

2。クラスは最初に実行されます Constructor,

つまり、静的に変更されたコード ブロックを実行し、静的に変更された変数に値を代入するだけです。静的に変更されたコード ブロックとクラス変数の実行順序は、ファイル内での出現順序に基づきます。そして、static StaticTest st = new StaticTest() が最初にランク付けされるため、new StaticTest() が実行されます。つまり、オブジェクトが初期化されます。

2.1. オブジェクトの初期化プロセス中に、メンバー変数 (コード)メンバー変数の実行順序も、最初に宣言した人が最初に実行するため、コード ブロックが最初にランクされます。

2.2 メンバー変数が実行された後、コンストラクター メソッドが実行されます。この時点で、 ,a=110,b=0;

3 が実行され、static StaticTest st = new StaticTest(); による非静的コードの初期化処理が終了します。静的コードは継続するため、出力は 1 です。

4. クラス全体のロードがここで終了し、コードが実行され、4 が出力されます。

次の文を見てください

public class StaticTest
 {

     public static void main(String[] args)
     {
         staticFunction();
     }
  
  
     static
     {
         System.out.println("1");
     }
  
     {
         System.out.println("2");
     }
  
     StaticTest()
     {
         System.out.println("3");
         System.out.println("a="+a+",b="+b);
     }
  
     public static void staticFunction(){
         System.out.println("4");
     }
  
     int a=110;
     static int b =112;
     static StaticTest st = new StaticTest();  //将这条语句放到最下面
 }

ステートメントを 1 つ変更するだけで、このコードの実行結果は

1
2
3
a=110,b=112
4
になります。

以上がJVM クラスロードメカニズムの詳細な紹介 (コード例)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。