ホームページ  >  記事  >  Java  >  Java 仮想マシン学習 - クラスロードメカニズム

Java 仮想マシン学習 - クラスロードメカニズム

黄舟
黄舟オリジナル
2017-03-18 17:51:551296ブラウズ

クラスロードメカニズム

JVMはクラスファイルをメモリにロードし、データを検証、変換、解析し、初期化して、最終的にJVMが直接使用できるJava型を形成するプロセスがロードメカニズムです。

クラスが仮想マシンのメモリにロードされてからメモリからアンロードされるまでのライフサイクルには、ロード、検証、準備、解決、初期化、使用 (使用)、およびアンロード (アンロード) が含まれます。 7 つの段階に分かれており、そのうち検証、準備、解析の 3 つの部分をまとめてリンクと呼びます。

ロード (ロード)、検証、準備、初期化、アンロードの 5 つの段階の順序は固定されていますが、解析段階は必ずしもこの順序で開始できるわけではありません。場合によっては初期化後に開始します。これはランタイム動的バインディング機能用です。これらのステージは通常、混合して実行され、通常は 1 つのステージの実行中に別のステージを呼び出すかアクティブ化することに注意してください。


ロード:

ロードフェーズは、通常「ロード」とも呼ばれます。これは主に次のように完了します。

1.クラスの名前" クラスのバイナリ バイト ストリーム

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

3. このクラスを表す java.lang.Class オブジェクトを生成しますJava ヒープ内で、メソッド領域内のこれらのデータへのアクセス ポイントとして

仮想マシンの仕様では、「このクラスを定義するバイナリ バイト ストリームを、 "クラスのフルネーム""。正確に言うと、どこでどのように取得するのかがまったく示されていません。例:

Zip パッケージからの読み取りは非常に一般的であり、最終的には将来の JAR、EAR、および WAR 形式の基礎になります。

共通アプリケーション アプレットをインターネットから入手します。

このシナリオで最も一般的に使用されるのは、動的プロキシ テクノロジであり、java.lang.reflect.Proxy では、特定の $Prxoy プロキシ クラスのバイナリ バイト ストリームを生成するために使用されます。インターフェース。

他の形式ファイルから生成、典型的なシナリオ: JSP アプリケーション

がデータベースから読み取ります。このシナリオは比較的まれで、一部のミドルウェア サーバー (SAP Netweaver など) は、プログラム コードを完成させるためにデータベースにプログラムをインストールすることを選択できます。間のクラスター分布。

クラスロードプロセスの他のステージと比較して、ロードステージ(準備的に言えば、ロードステージでクラスのバイナリバイトストリームを取得するアクション)は、開発期間中最も制御可能なステージです。提供するシステムを使用します。クラス ローダー (ClassLoader) によって完了することも、ユーザー定義のクラス ローダーによって完了することもできます。開発者は独自のクラス ローダーを定義することで、バイト ストリームの取得方法を制御できます。

ロードフェーズが完了すると、仮想マシンの外部にあるバイナリバイトストリームは、仮想マシンが要求する形式に従ってメソッド領域に格納されます。メソッド領域のデータ格納形式は、仮想マシンの実装によって定義されます。仮想マシンは領域の特定のデータ構造を規定しません。次に、java.lang.Class クラスのオブジェクトが Java ヒープ内にインスタンス化されます。このオブジェクトは、プログラムがメソッド領域内のこれらのタイプのデータにアクセスするための外部インターフェイスとして機能します。ロード フェーズとリンク フェーズの一部 (バイトコード ファイル形式検証アクションの一部など) はインターリーブされています。ロード フェーズはまだ完了しておらず、リンク フェーズが開始されている可能性がありますが、これらのアクションはまだロード フェーズの間に挟まれています。ステージの内容とこれら 2 つのステージの開始時間は依然として固定された順序を維持します。


検証:

検証は、リンクフェーズの最初のステップです。このステップの主な目的は、バイトストリームに含まれる情報を確認することです。 class ファイルは現在の仮想化に準拠しており、仮想マシンの要件を満たしており、仮想マシン自体のセキュリティを危険にさらすことはありません。

検証フェーズには主に、ファイル形式検証、メタデータ検証、バイトコード検証、シンボル参照検証の 4 つの検証プロセスが含まれます。

1. ファイル形式の検証

クラス ファイル形式の仕様を検証します。たとえば、クラス ファイルがマジック 0xCAFEBABE で始まるかどうか、メジャー バージョン番号とマイナー バージョン番号が現在の仮想マシンの処理範囲内にあるかどうかなどです。

2. メタデータの検証

この段階では、バイトコードによって記述された情報に対してセマンティック分析を実行し、記述された情報が Java 言語仕様の要件を満たしていることを確認します。検証ポイントには、このクラスに親クラスがあるかどうか (java.lang.Object を除くすべてのクラスには親クラスがある必要があります)、このクラスが継承が許可されていないクラス (final によって変更された) を継承しているかどうか、およびこのクラスの親クラスは、親クラスまたはインターフェイスに実装する必要があるすべてのメソッドを実装していますか?

3. バイトコードの検証

データ フローと制御フローの分析を実行します。この段階では、クラスのメソッド本体が検証され、分析されます。この段階のタスクは、検証対象のクラスのメソッドがセキュリティを危険にさらすアクションを実行しないことを確認することです。実行時の仮想マシン。たとえば、アクセス メソッド本体の型変換が有効であることを確認します。たとえば、サブクラス オブジェクトを親クラスのデータ型に割り当てることは安全ですが、親クラスのオブジェクトをサブクラスのデータ型に割り当てることはできません。ジャンプ コマンドがメソッド本体の外部のバイトコード コマンドにジャンプしないことを確認してください。

4. シンボル参照の検証

シンボル参照内の文字列で記述された完全修飾名が対応するクラスを見つけることができるかどうか、シンボル参照クラス内のクラスかどうか、フィールドとメソッドのアクセス可能性 (プライベート、プロテクト、パブリック、デフォルト)は、現在のクラスからアクセス可能です。


準備:

準備フェーズは、クラス変数に正式にメモリが割り当てられ、クラス変数の初期値がメソッド領域に割り当てられるフェーズです。この段階で混乱しやすい点が 2 つあります。まず、この時点でのメモリ割り当てには、オブジェクトがインスタンス化されるときにオブジェクトに従うインスタンス変数ではなく、クラス変数 (静的変更変数) が含まれます。 Java ヒープ。次に、ここで言う初期値は「通常」データ型のゼロ値です。

public static int value = 12; とすると、準備フェーズ後の変数値の初期値になります。は 12 ではなく 0 です。この時点では Java メソッドが実行されておらず、プログラムのコンパイル後に 123 に値を割り当てる putstatic 命令がクラス コンストラクターの () メソッドに格納されるため、値が割り当てられます。 ~ 12 アクションは初期化フェーズ中にのみ実行されます。

上記の「通常の状況」では、初期値はゼロですが、一部の特殊な場合と比較して、クラスフィールドのフィールド属性テーブルに ConstantValue 属性がある場合、変数の値は ConstantValue 属性に初期化されます。準備段階では、指定された値は次のように定義されます:

public static Final int value = 123;

準備段階では、javac は value の ConstantValue 属性を生成します。 ConstantValue を 123 に設定します。


解析:

解析フェーズは、仮想マシン定数プール内のシンボル参照を直接参照に置き換えるプロセスです。

シンボリック参照: シンボリック参照は、参照されるターゲット オブジェクトを説明するためのシンボルのセットであり、使用時にターゲットを明確に特定できる限り、シンボルは任意の形式のリテラルにすることができます。シンボリック参照は、仮想マシンによって実装されるメモリ レイアウトとは何の関係もなく、参照されるターゲット オブジェクトは必ずしもメモリにロードされる必要はありません。

直接参照: 直接参照は、ターゲット オブジェクトを直接指すポインター、相対オフセット、またはターゲットを間接的に特定できるハンドルです。直接参照は、仮想マシンのメモリ レイアウトの実装に関連しています。異なる仮想マシン インスタンス上の同じシンボル参照から変換された直接参照は、通常、同じではありません。直接参照がある場合、参照ターゲットはメモリ内にすでに存在している必要があります。

仮想マシンの仕様では、解析フェーズが発生する特定の時間を規定していません。必要なのは、anewarry、checkcast、getfield、instanceof、invokeinterface、invokespecial、invokestatic、invokevirtual、multianewarray、new、putfield、putstatic などの 13 個の操作シンボルのみです。参照されるバイトコード命令の前に、それらの命令で使用されるシンボル参照が最初に解析されるため、仮想マシン実装は、クラスがローダーによってロードされるときに定数プール内のシンボル参照を解析するか、シンボル参照が完了するまで待つかを必要に応じて判断します。使用される直前に解決されます。

解析アクションは主に、クラスまたはインターフェイス、フィールド、クラス メソッド、およびインターフェイス メソッドの 4 種類のシンボル参照に対して実行されます。コンパイル済み定数プール内の 4 つの定数タイプ CONSTANT_Class_Info、CONSTANT_Fieldref_Info、CONSTANT_Methodef_Info、および CONSTANT_InterfaceMethoder_Info にそれぞれ対応します。

1. クラスとインターフェイスの分析

2. フィールドの分析

4. インターフェイス メソッドの分析

クラスの初期化フェーズはクラスの最後のステップですロードプロセスでは、クラス変数にシステムが必要とする初期値が割り当てられます。初期化フェーズでは、プログラマがプログラムを通じて作成した主観的な計画に従って、クラス変数とその他のリソースが初期化されます。別の観点から表現することもできます: 初期化 この段階は、class constructionor() メソッドを実行するプロセスです。初期化プロセスは、次の 4 つの状況でトリガーされます。


1. new、getstatic、putstatic、または invokestatic の 4 つのバイトコード命令が発生した場合、クラスが初期化されていない場合は、最初にその初期化をトリガーする必要があります。これら 4 つの命令を生成する最も一般的な Java コード シナリオは、 new キーワードを使用してオブジェクトをインスタンス化すること、クラスの静的フィールドを読み取りまたは設定すること (final によって変更され、定数プールに入れられた静的フィールドを除く) です。コンパイラ) )、およびクラスの静的メソッドを呼び出すとき。

2. java.lang.reflect パッケージのメソッドを使用してクラスへのリフレクション呼び出しを行う場合

3. クラスを初期化するときに、その親クラスが初期化されていないことが判明した場合は、親クラスの最初の初期化

4. jvm が起動すると、ユーザーは実行するメイン クラス (main メソッドを含むクラス) を指定し、仮想マシンは最初にこのクラスを初期化します。

上記の準備フェーズでは、値は public static int value = 12 です。準備フェーズが完了した後の value の値は 0 で、このステージが完了した後、クラス コンストラクター () メソッドが呼び出されます。

*クラス コンストラクター() メソッドは、クラス内のすべてのクラス変数の代入アクションをコンパイラーが自動的に収集し、ステートメントを静的ステートメント ブロック (静的ブロック) にマージすることによって生成されます。コンパイラーによる収集の順序。ソース ファイル内でステートメントが出現する順序によって決定され、静的ステートメント ブロックは、静的ステートメント ブロックよりも前に定義された変数にのみアクセスできますが、前の静的ステートメント ブロックで値を割り当てることはできますが、その後に定義された変数にはアクセスできません。 。

*クラス コンストラクター () メソッドは、クラス コンストラクター (インスタンス コンストラクター () メソッド) とは異なり、親クラス コンストラクターを明示的に呼び出す必要はなく、仮想マシンは必ず呼び出します。サブクラス内に存在する() メソッドが実行される前に、親クラスの () メソッドが実行されます。したがって、仮想マシンで最初に実行される () メソッドのクラスは java.lang.Object である必要があります。

*親クラスの () メソッドが最初に実行されるため、親クラスで定義された静的ステートメントがサブクラスの変数代入操作よりも優先されることを意味します。

*() メソッドはクラスまたはインターフェイスには必要ありません。クラスに静的ステートメントがなく、変数代入操作もない場合、コンパイラーはこのクラスに対して () を生成する必要はありません。方法。

*静的ステートメント ブロックはインターフェイスでは使用できませんが、インターフェイスとクラスでは不可能なのは、インターフェイスの () メソッドを実行するときに、最初にインターフェイスの () メソッドを実行する必要がないことです。親インターフェイス。親インターフェイスは、親インターフェイスで定義された変数が使用される場合にのみ初期化されます。さらに、インターフェイスの実装クラスは、初期化中にインターフェイスの () メソッドを実行しません。

*仮想マシンは、マルチスレッド環境でクラスの () メソッドが正しくロックされ、同期されていることを保証します。複数のスレッドが同時にクラスを初期化する場合、1 つのスレッドのみが <このクラスの clinit>() メソッドを実行すると、他のスレッドはブロックして、アクティブなスレッドが () メソッドの実行を完了するまで待機する必要があります。クラスの () メソッドに長時間実行される操作がある場合、複数のプロセスがブロックされる可能性があります。

上記は Java Virtual Machine Learning - Class Loading Mechanism の内容です。その他の関連コンテンツについては、PHP 中国語 Web サイト (www.php.cn) に注目してください。

関連記事:

Java仮想マシンを詳しく解説

Java仮想マシンを深く理解する

Java仮想マシン学習 - オブジェクトのメモリ割り当てとリサイクル

Java仮想マシン学習 -オブジェクトアクセス

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