仮想マシン[JVM]クラスローディング機構

クラスローダは何である
メモリにクラスファイルからロードされたデータのクラスを記述するための仮想マシン、およびデータの検証は、変換と解析を初期化は、最終的には直接仮想マシンで使用できるJava型を形成し、これは、仮想マシンのクラスローディング機構であります。

「[JVM]クラスファイル構造」我々はコンパイル済みのクラスファイル(.classファイル)を書き込みした後に、クラスファイルの構造、すなわちJavaコード(.javaファイル)の話。この章では、メモリ内にクラスファイルをロードし、最終的には、直接Javaの型を使用して仮想マシンを形成する方法について説明します。


1、クラスのロード時間

  • クラスのライフサイクル
クラスのライフサイクル

固定されており、ローディング、検証、製造、およびアンロード5つの初期化シーケンス、すなわち、クラスローディングプロセスは、この順で始まる(この順又は完全に行われていないが、クロスのすべての段階のために)。
いくつかのケースでは解決フェーズは、初期化後に開始されます:バインディング(動的バインディングまたは遅延バインディング)Javaランタイムをサポートするために。

概念が追加されました:動的バインディング
Bindingクラスを参照し、方法(メソッド本体)を関連付けるメソッドを呼び出します。Javaの場合、結合は、スタティックバインディングと結合動的に分割されています。
スタティックバインディングは最後に、この方法が唯一の最終的な、静的、プライベート及び建設方法は静的結合されたクラスのメソッド、Javaメソッドであることを知って既にコンパイルのプロセスを指します。動的バインディングプログラムを実行する方法に種類の特定のオブジェクトをバインドするための手段。Java仮想マシンが動作中にオブジェクトの種類を決定し、適切なメソッドを呼び出すことができます。

  • タイミング負荷
    の仮想マシンの仕様は、第一段階のロード制約を強制しませんがとき始めたが、ために提供し、唯一の5例は(読み込み、検証しながら、準備は当然その前に行われる必要があります)、すぐにクラスを初期化する必要があります。次のようにこれらの5つがあります:
  • この4バイトコード命令、およびクラスが初期化されていなかったとき、新しいgetstatic、putstatic、invokestaticに遭遇します。これは、Javaコードの一般的なシナリオがある4つの命令を生成しますの使用新しいキーワードは、オブジェクトをインスタンス化する際に読み取りまたはクラスの静的フィールドを設定する(最終結果はコンパイル時の静的フィールドの定数プールに置かれている以外、修正)とき、クラスの静的メソッド呼び出しの時間を。
  • 使用するjava.lang.reflectクラスのメソッドパッケージ反射コールクラスが初期化されていない場合は、時間を、あなたは初期化をトリガーする必要があります。
  • クラスを初期化するとき、あなたが親クラスが初期化されていない見つけた場合、あなたは必要な親クラストリガするために初期化を。
  • 仮想マシンが起動すると、ユーザは、このマスタークラスを初期化するために、仮想マシンを(クラスのメインメソッドを含む)を実行するメインクラスを指定する必要があります。
  • 場合動的言語サポートJDK1.7を使用する場合java.lang.invoke.MethodHandle、最終的な分析結果の例はREF_getStaticREF_putStaticREF_invokeStaticメソッドハンドル、およびこのハンドルクラスに対応するメソッドが初期化されていないので、初期化は、それをトリガするために必要とされます。

知られるクラスの動作のために、これらの5つのシナリオアクティブな参照は、他に知られているように、初期化クラスを誘発しない受動参照受動的参照は以下の通りで:①サブクラス参照により親クラスの静的フィールドを、唯一の親クラスを初期化するためにつながる、サブクラスの初期化をもたらさないであろう。②定義によるクラスの配列を参照するために、初期化クラスをトリガしません(しかし、配列の初期化をトリガする);③コンパイル時定数は、定数プールクラスに入金されますが呼び出され、自然のクラス定数の定義への直接参照が存在しない、それは初期クラス定義された定数を誘発しないであろう。

違い初期化インターフェースの初期化クラス:親が本当にインターフェイスを使用する場合にのみ、クラスの初期化、それは親クラスが初期化されている必要がありますが、初期設定時に一つのインタフェース、インタフェースは、すべての初期化を完了するために、親を必要としません。初期化。


クラスのロードプロセス

1、ロード

ロード・フェーズでは、仮想マシンは、3つのことを行う必要があります。

  • バイナリバイトストリームを確保
    かかる取得の完全修飾名に合わせて、クラスのバイナリストリーム・ファイル、ZIPパッケージ、ネットワークの主な発生源は、計算実行時に生成、そのようJSP、データベース)などの他のファイルを生成し、
  • バイナリバイトストリームは、メソッド領域に格納されている(特定のフォーマットに従って格納されています)。
  • メモリ内に生成するjava.lang.Class被写体
    このクラスの各種データ入力領域にアクセスするために、クラスオブジェクトのメソッドとして機能する、仮想マシンは、オブジェクトが処理領域に位置しているホットスポット。
2、検証

検証の目的は、クラスに含まれる情報のバイトストリームファイルが自分自身の安全を危うくしません、現在の仮想マシンと仮想マシンの要件を満たしていることを確認することです。検証フェーズは4段階以下のおおよそ検査作業を完了されます。

  • ファイル形式がチェック
    のみ、この段階を通して、位相をバイナリバイトストリームに基づいていることを確認するために、検証フェーズ領域法の背後に保管するための処理領域メモリに入るバイトストリームは、ストレージ構造に基づいています行って、それが直接バイナリストリームを動作しません。含む:マジックナンバーかどうか0xCAFEBABE、初めにメジャーとマイナーバージョン番号など、現在の仮想マシン処理の範囲内。

  • メタデータチェック
    継承クラス(継承することを許可されていないかどうか、クラスは親クラスを持っているかどうか:目的は、そのメタデータ情報は、Java言語仕様に準拠していない保証することであるなど、存在しないfinal実現するかどうかを、修飾クラス)または親インタフェースを達成するために必要なすべてのメソッド、フィールドと矛盾場合、親クラスのメソッド。

  • 検証バイトコード
    プログラムのセマンティクスを決定するために、データ・フローと制御フロー解析によって達成される目的が正当である、論理的、すなわち、検証クラスメンバーの分析方法。

  • シンボリック参照の検証
    場合シンボル参照チェックに一致以外のクラス情報自体には、直接参照(解析相)に仮想マシンで発生します。含む:①シンボリック参照は、対応するクラスがフルネーム列記述によって定義されているかどうかを見出すことができる。②フィールド記述子と単純なメソッド名と説明フィールド指定されたクラスに沿った方法があるか否か、クラス参照記号③フィールドは、現在のクラスにアクセスできるかどうか、アクセス方法。

検証フェーズは非常に重要であるが、実行を繰り返し使用して検証されている場合は、必ずしも必須ではない段階、コード(私が書いたコードだけでなく、サードパーティのパッケージ)、使用することを検討-Xverify:noneするクラス検証措置のほとんどをシャットダウンします時間仮想マシンのクラスのロードを短縮します。

3、準備ができて

準備フェーズは以下のとおりです。初期値を設定し、クラス変数とクラス変数のためのメモリを割り当てますこれらの変数は、メモリによって使用されるメソッド領域に割り当てられました。注意することががある:①静的クラス変数は、インスタンス変数は、オブジェクトがインスタンス化されたJavaヒープ・オブジェクトと一緒に割り当てられるインスタンス変数を含まない、可変であるように改変されている。②初期値は、通常、データを参照しますゼロ値の種類、それがある場合にstatic final、変数の種類、値が直接その変数に指定されたコードに割り当てられます。

private int value = 123;//不会为该变量分配内存
private static int value = 123;//会为该变量分配内存并赋为零值(0) private static final int value = 123;//会为该变量分配内存并赋值为123 
4、構文解析

パース舞台作品:直接の参照を置き換えるプロセスへのシンボリック参照の仮想マシン定数プール。クラスまたはインタフェース、フィールド、メソッドのクラスのための主要な分析操作、インターフェースメソッド、タイプ法、及び方法のハンドル点修飾子を呼び出します。
符号:参照対象を記述するシンボルのセット、リテラルシンボルは、任意の形態であってもよく、宛先インターフェイス限り使用するように明確に配置することができます。
直接参照:オブジェクトへの直接ポインタ、相対オフセットまたは間接的にターゲットを処理するためにターゲット。

  • クラスまたはインタフェースが解析
    クラスまたはインタフェースCを直接参照、全体のプロセスは、3つのプロセスに分割されている現在のコードがDクラス、Nシンボリック参照を解決することが想定される:
    ①タイプが配列C、仮想マシンでない場合Nは、クラスをロードするためにDクラスローダに渡された完全修飾名を表します。ロード処理では、このクラスと他のクラスの父にロードすることができます。
    Cは、配列要素の型をロード①によれば、アレイのタイプ、型オブジェクトの要素の配列である場合②、仮想マシンは、この配列内のオブジェクトと要素のアレイを生成する大きさを表します。
    これらのステップが異常だった③、その後、Cは実際には仮想マシンで有効なクラスまたはインタフェースとなっています。しかし、また、アクセスチェックのために。

  • フィールド分析の
    フィールドシンボル参照が解決参照構文解析は、分析通常想定に、最初のシンボルクラスまたはインタフェースのフィールドが属するであろう、このフィールドのクラスが属するか、または次の順序で後続のフィールドのための仮想マシンC Cによって表さインターフェース検索:C自体、Cで実装各インターフェイスまたは親インターフェース(再帰的)、Cは(再帰的に)親クラスを継承します。
    同じ名前のフィールドはまた、複数のインタフェースとその親クラスで同時にCインタフェースと親クラス、またはで表示された場合、コンパイラはコンパイルを拒否します。

  • クラス分析法

  • インターフェイスメソッドの解決

5、初期化

初期化:本当にクラスで定義されたJavaコード(バイトコードの)実行を開始します。
準備段階では、変数は、プログラマ主観的な計画で指定された手順に従って、クラス変数や他のリソースを初期化するために、システム要件に一度、および初期化フェーズで初期値が代入されています:初期化フェーズは、実装クラスのコンストラクタのある<clinit>プロセスアプローチ<clinit>見ることができますclass init的简写

<clinit>方法:

<clinit>合わせ、すべてのクラスと、ブロック内の文から文の静的変数のコンパイラのクラスの代入演算によって自動的に収集され、コレクションの順序は、ステートメントの決定により、ソース・ファイル内の出現順です。注意:前の文のブロック内で定義された静的変数に静的文ブロックのアクセスのみ、その背後にある変数の定義を、割り当てることができますが、次のようにアクセスすることはできません。

public class Test{
    static { i = 0;//赋值可以通过编译 System.out.print(i);//这句访问则会导致编译器报错:非法向前引用 } static int i = 1; } 

<clinit>メソッドとクラスのコンストラクタ(インスタンスコンストラクタ<init>)異なる、それが明示的に親クラスのコンストラクタを呼び出すことはありません、仮想保証サブクラス<clinit>の実行方法の前には、親クラスの<clinit>メソッドがすでに実装されていますそのため、最初の仮想マシンを実行する<clinit>確かにクラスメソッドをjava.lang.object

親クラスとして③ <clinit>方法を最初に実行するので、可変割当における親クラスブロック優先サブクラスで定義された静的ステートメントが実行されます

<clinit>クラスが文の静的ブロックでない場合メソッドのクラスまたはインタフェースが、必要ではないが、クラス変数のない割り当てがない、コンパイラは、クラスに対して生成されなくてもよい<clinit>方法。

⑤インタフェースブロック静的ステートメントを使用することはできないが、それでも変数割り当てが初期化され、したがって、同じクラスインターフェースを生成<clinit>する方法を。違いは、インタフェースが実装している<clinit>親クラスの実装を必要としない<clinit>親を使用してインターフェイスで定義された変数は、親インターフェースの初期化である場合にのみ、方法。さらに、インタフェースの実装クラスの初期化は、場合インターフェイス行われない<clinit>方法

⑥仮想クラス確保する機会<clinit>の方法が正しく、マルチスレッド環境での同期がロックされています。複数のスレッドがクラスを初期化する場合は、唯一のこのタイプ実行するためのスレッドが存在するであろう<clinit>方法を、他のスレッドはアクティブスレッドが実行されるまで待機する必要がブロックされている<clinit>方法は、一度だけ、次の(同じクラスローダタイプの初期化を完了すると)。クラスがいる場合<clinit>の動作は非常に時間のかかる方法を持っている、複数のプロセスが閉塞につながることができます。


クラスローダ

演技のクラスローダ:クラスの完全修飾名によって、バイナリバイトストリームの記述を取得しますこのアクションは、アプリケーションが必要なカテゴリを取得する方法を決定できるようにするために、外部のJava仮想マシンで実装されています。クラスローダのクラス階層に分け輝き、OSG​​iの、熱い展開、コードの暗号化、およびその他の分野

クラスとクラスローダー

任意のカテゴリについて、あなたはそれがクラスローダとJava仮想マシンでその独自性を持つクラス自体によって確立されたロードする必要があるだけでこの2つのクラスでは、「等しい」は、同じクラスからのものかどうかを2つのクラスを比較するには:前提ローダが理にかなっている、または同じファイルから、この2つのクラスクラスならば、限り、異なるそれらをロードするクラスローダとして、仮想マシンにロードされ、そして2つのクラスが等しく確実ではないということです。

オブジェクト・クラスのクラスを含む、等しいが、本明細書で言及クラス表現equals()方法を、isAssignableFrom()方法、isInstance()方法は、の使用を含む、結果を返すinstanceofキーワードオブジェクトが属する等場所を決定する関係を行います。

親委譲モデル

Java仮想マシンの視点が懸念されることから、2つのだけ異なるクラスローダがあります:
①クラスローダを起動します。Bootstrap ClassLoader独自の仮想マシンの一部。
外部の仮想マシンとは独立して、すべての抽象クラスから派生:②他のクラスローダjava.lang.ClassLoaer

Java開発者の視点からは、クラスローダは、より詳細に分けることができ、それが4種類に分かれています。

  • ブートクラスローダBootstrap ClassLoader
    責任はに格納され<JAVA_HOME>\libたディレクトリ、またはによって-Xbootclasspathパスで指定されたパラメータ、および仮想マシンの識別ライブラリは、仮想マシンのメモリにロードされます。開発者が直接ブートクラスローダに必要な委任した場合にロードされ、ブートクラスローダを参照し、直接しないnull代わりにします。
  • 拡張クラスローダは、Extention ClassLoader
    ロードを担当する<JAVA_HOME>\lib\extディレクトリを、またはによってjava.ext.dirs全てのライブラリ、パスを指定したシステム変数の開発者が直接使用するクラスローダを拡張することができます。
  • アプリケーションクラスローダは
    また、ユーザークラスのロードパスをロードし、システムクラスローダと呼ばれることがclasspath一般的に、アプリケーションが何もカスタム独自のクラスローダを持っていない場合、開発者は、直接クラスローダを使用することができ、ライブラリによって指定され、これは、デフォルトのクラスローダのアプリケーションの場合です。
  • カスタムクラスローダ

アプリケーションは、4種ローダーによって相互にロードされ、それらの間の関係は、下図のように:

親クラスローダ委譲モデル

クラスローダ委譲モデルとして知られているこの親クラスローダの階層:ブートクラスローダのトップに加えて、クラスローダの残りの部分は、自身の親クラスローダを持っている必要があります。クラスローダ間の親子関係は、ここで一般的に継承関係を達成しますが、親ローダーとコード多重関係の組み合わせを使用することはありません。

親委任モデルの作業プロセス:クラスは、クラスをロードするための要求を受信した場合、それは最初にこのクラスをロードしようとするために所有しますが、クラスローダの各レベルを完了するために、親クラスローダにこの要求を入れていないですそれはそうです。したがって、すべての要求が親は、彼らがロード要求を完了できない場合にのみ、ブートクラスローダ、ローダフィードバックを開始するには、トップからロードされ、サブクラスは自分自身をロードしようとします。

親委任モデルを使用する利点:Javaクラスをそのクラスローダと一緒の関係で優先度レベルで、例えば、どの2つのことを確実にするためjava.lang.Obejctのクラス、Javaの型システムは、最も基本的な動作を保証しないように、確実にJavaプログラムの安定した動作。

モデルの実装コード(両親のデリゲートjava.lang.ClassLoaderloadClass()メソッド):

protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{
    synchronized (getClassLoadingLock(name)) {
        // 首先检查该类有没有被加载过 Class<?> c = findLoadedClass(name);// if (c == null) {//说明之前该类没加载过 long t0 = System.nanoTime(); try { if (parent != null) { //让父类加载器取加载 c = parent.loadClass(name, false); } else { //让启动类加载器去加载 c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { //发生ClassNotFoundException异常,则说明父类加载器没有加载成功 } if (c == null) {//为空,说明父类加载器没有加载成功 long t1 = System.nanoTime(); //调用自身findClass(String name)方法去进行类加载 c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; } } 
両親ダメージ委譲モデル(// TODO)
  • 最初は、
    その前にJDK1.2リリース親委任モデルの前に現れ起こります。カスタム・ユーザー・クラスローダーは、上書きするとloadClass()自身のクラスローダロジックを達成するための方法を。さて、この方法は、キーモデルの実装委任両親ですが、ユーザーが書き換えることができるのでJDK1.2はもはや、ユーザーはこのメソッドをオーバーライド提唱した後、findClass()方法を。
  • 第二の
    モデル自体が欠陥につながる、このような問題は、JNDIサービスのような基本クラスを、(呼び出すためにユーザコードを解決することができませんJava Naming and Directory Interface)。
  • 第三
    あるいはホットコード、熱モジュールの展開。

「Java仮想マシンの深い理解」からのコンテンツ:VMクラスローディング機構



著者:maxwellyue
リンクします。https://www.jianshu.com/p/03dec949d01b
出典:ジェーンの本

おすすめ

転載: www.cnblogs.com/xiaoshen666/p/11258567.html