詳細なJava仮想マシン(x)の------クラスのロード処理

  前回の記事では、我々は、Javaの記述詳細なクラスファイル構造ファイルは、それを指示するために仮想マシンによってメモリにロードされるか、そしてクラスを?クラスのロード処理を-それがこのブログを紹介するのです。

1、クラスのライフサイクル

  クラスからメモリをアンロードする仮想マシン、アップを開始するためにメモリにロードされ、次のように、そのライフサイクルプロセスは、次のとおりです。

  

  言うことである5つの部分(ローディング、検証、製造、初期化、アンインストール)順序が決定され、上記の赤色で示し、クラスローディングプロセスは、この順に段階的に開始することができます。これらの段階は、通常、互いに交差混合が行われるので、ここでは「スタート」は、典型的段階の過程で別のステージを呼び出す、または「完了」「の」ステップバイステップではありません。

2、負荷

  「ロード」相は「クラスローダ」ライフサイクルの最初のフェーズです。ロード・フェーズでは、仮想機密完全に次の三つのこと:

  ①、クラスの完全修飾名によって定義されるようなバイナリバイトストリームを取得します。

  ②、ランタイムデータ構造領域メソッドに静的記憶構造を表すバイトストリーム。

  ③、そのようなデータの入口領域へのアクセスの方法として、このクラスのJavaヒープjava.lang.Classオブジェクトの代表を生成します。

  PS:このクラスのクラスの完全修飾名が格納されている絶対パスとして理解することができます。メソッド領域主に店舗のクラス情報に使用されるランタイムデータ領域以前に定義されJDK1.7、JDK1.8以降のメタデータエリア(メタスペース)に変更するには、Java仮想マシン、定数、静的変数、時間コンパイラがロードされていますコンパイルされたコードやその他のデータ。-詳しくはこちらシリーズの第二の記事を参照してください。実行時のメモリ構造

  また、我々は最初のポイントを見て - という名前のクラスバイナリストリームの定義によって、このような許可を得るために、そこに明示的に取得する場所を指定していないとどのようにそれが明確に定義されていない取得するために、我々はクラスファイルからのものでなければなりません買収しました。これに基づき、Java開発プロセス、トリックのすべての種類を再生するには、この分野で創造的な開発者:

  1は、ZIPパッケージから読み取ります。これは、JAR、EAR、WAR形式の背後にあるベースと呼ばれています。

  図2は、ネットワークから取得しました。典型的なアプリケーションは、アプレットです。

  3、計算して、実行時に生成されます。これは、動的エージェント技術です。

  4は、他のファイルによって生成されます。たとえば、JSPアプリケーション。

  図5は、データベースから読み取ります。

  ローディング段階が完了した後、処理ゾーン内の仮想マシンが必要とする形式に格納されたバイトのバイナリストリーム、およびJavaクラスjava.lang.Classオブジェクトインスタンス化スタックによれば、仮想マシンの外部に、このオブジェクトは、プログラムとしてアクセスしますインターフェースのデータ領域外の方法のこれらのタイプ。

  接続フェーズ段の装填部と、なお、(例えば、チェックバイトコード形式のファイルの一部)は、クロスため、ローディング・フェーズが完了していない、接続フェーズが始まっている可能性があります。

確認します3.

  最初のステップは、役割があるし、接続フェーズを検証することであるクラスに含まれる情報のバイトストリームファイルが自分自身の安全を危うくしません、現在の仮想マシンと仮想マシンの要件を満たしていることを確認してください。

  私たちは、Java言語自体が原因コンパイラで比較的安全であると言う、境界外のデータの配列にアクセスするための純粋なJavaコードは、コードの行へのジャンプが存在しないと、などのコンパイラによって拒否されることになります。しかし、我々は以前、クラスファイルは、コンパイルされたJavaソースコードから来る必要はありません、あなたはクラスがバイナリエディタでファイルを直接書き込むために、あなたは非常に高速なハードウェアを含む任意の手段を、使用することができると述べています。

  仮想マシンが入ってくるバイトストリームをチェックしないのであれば、バイトストリームは、有害なロードされ、システムがクラッシュする原因になります。しかし、どのような検査のための仮想マシンの仕様、検査、チェックは明確に定義されていないか、別の仮想マシンの実装では、すべて異なっていてもよいが、一般的な検査は、次の4つの側面を完了します。

①、ファイル形式の検証

  クラスファイル形式に準拠したバイトストリームをチェックして、仮想マシン処理の現在のバージョンであることができます。

  まず、マジックナンバー0xCAFEBABEの始まりかどうか。

  第二に、メジャーとマイナーバージョン番号が現在の仮想マシン処理の範囲内です。

  定数プール内の第三の、サポートされていないタイプの定数が存在する場合(一定のタグチェックマーク)定数

  第四に、存在しないか、定数のタイプを満たしていない一定のポイントがあるかどうかの一定の様々な指標値ポイント。

  第五に、UTF8でエンコードされたデータCONSTANT_Utf8_infoタイプ定数の不遵守するかどうか。

  六は、削除またはその他の追加情報がなければ、クラスファイルと自身のさまざまな部分の文書がある場合。

  これらは、はるかに、当然のチェックの内容の一部です。これらのチェックバイトストリームは、メモリ領域の格納方法に入りした後、チェック方法の背後にある次の3つの段階が実行領域の記憶構造に基づいています。

②、メタデータの検証

  第二段階は、主にJava言語仕様を記述する情報の遵守を確実にするために、情報の意味解析バイトコード記述されています。

  クラスは親クラスを持っている場合、最初は、(java.lang.Objectクラスを除いて、すべてのクラスは、親クラスを持っている必要があります)。

  このクラスの継承の親クラス(最終修正クラス)を継承することを許可されていない場合は、2番目、。

  第三に、クラスがその親クラスまたはインタフェースの実装要件のすべてに共通のメソッドを実装するかどうかを、抽象クラスではない場合。

  この方法は、親と矛盾であるか否かを第四のクラスのフィールド、(例えば親クラスの最終的な分野をカバーする、またはコンプライアンス発生の過負荷)

③、バイトコード検証

  第三段階は、最も複雑性を検証する全体バイトコード検証フェーズであり、主制御フローとデータフロー分析です。クラスはメソッドが実行時に検証されることを保証するために、分析のための方法を上演する仮想マシンのセキュリティを危険にさらす行為をすることはありません。

  まず、いつでも命令のオペランドスタックとデータ型コードシーケンスが協力できることを確認します。例えば、Shiqueによる長いタイプは、ローカル変数テーブルにロードされ、int型のデータオペランドスタックに配置されないであろう。

  第二に、メソッド本体以外の命令のバイトコードジャンプ命令にジャンプしないことを確認します。

  第三に、ボディ型変換方法が有効であることを保証します。例えば、安全で親クラス・データ・タイプへのサブクラスオブジェクトを割り当てます。しかし、親オブジェクトは、サブクラスのデータ・タイプに割り当てられた、あるいは正当されていない、完全に無関係な入力に割り当てられました。

④、シンボリック参照の検証

  シンボル参照情報は、主整合性チェックの自体以外の認証(定数プール基準シンボル)に基づいている、チェックは、典型的には、以下が必要です。

  シンボル参照は、完全修飾名で対応するクラスに記述された文字列で見つけることができます。

  第二には、メソッドやフィールドとフィールド記述子に沿った方法がある場合は、指定したクラスで単純名を記述しました。

  第三に、アクセスクラス、フィールドやメソッドの参照現在のクラスにアクセスできるかどうか(プライベート、保護、公共、デフォルト)内のシンボル。

4、準備ができて

  準備フェーズは、正式にあるクラス変数に割り当てられたメモリと設定クラス変数の処理で割り当てられたメモリ領域である初期値の段階。

  注意:

  まず、上記インスタンス変数が含まれていない、静的変数を変更されるクラス変数であることを特徴とします。オブジェクトはヒープにインスタンス化されるオブジェクトとともにインスタンス変数の割り当て。

  第二に、初期値は、一部のデータ型のデフォルト値を参照します。初期値としての基本データ型(基準型ヌルの初期値):

  

 

   例えば、公共の静的int型値= 123の定義。準備段階の後に、値の値が0の代わりに123であるので、値123は、プログラムがコンパイルされた後に、割り当てられた初期化段階中に実行されているクラスのコンストラクタメソッドに格納されています。しかし、特別な場合、このような公共の最終的な静的int型値= 123の定義などの属性、最終変更がある場合、準備段階の後、値は123に割り当てました。

5、決意

  ステージを解析すると、直接参照を交換するプロセスのための基準の記号定数プールへの仮想マシンです。

  基準シンボル(シンボル参照):参照に記載されたシンボルの特定のセットへのシンボル参照は、リテラルシンボルは長い標的ほど明確なターゲットを用いることができることができる、任意の形態であってもよいです。シンボル参照は、仮想マシンを達成するためのメモリレイアウトとは何の関係もない、目標は必ずしもメモリにロードされた基準ではありません。

  直接参照(直接参照):直接参照がオブジェクトへの直接ポインタ、相対オフセットまたは間接的にハンドル標的を標的とすることができます。異なるシンボルの仮想マシンインスタンスのメモリレイアウトへの直接参照が仮想マシンを用いて達成され、そして参照が直接翻訳引用は一般に同じではありません。あなたが直接参照を持っている場合は、目標基準は、既にメモリに存在している必要があります。

  それぞれ定数プールCONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info、CONSTANTS_InterfaceMethodref_info定数の4種類に対応するクラスまたはインタフェースのための主要な分析操作、フィールド、メソッド、クラス、インタフェースメソッドリファレンス4つのシンボル、。

図6に示すように、初期化

   クラス初期化フェーズは、プロセスの前に、ローディング・フェーズの最後のステップであるが、第一段階が関与する定義されたクラスローダ外部からロードすることができ、プロセスの残りの部分は、完全にユーザによって仮想マシンによって支配され、制御されます。初期化フェーズでは、その後、クラス定義(またはバイトコード)でのJavaコードの実際の実装を開始します。

  準備フェーズでは前述した、クラス変数は既に、初期化段階が、初期化変数に符号化およびリソースのプログラマを初期値が割り当てられています。

  換言すれば、初期化フェーズは、クラスコンストラクタ<clinit>()メソッド中に行われます

  ①、<clinit>()メソッドは自動的にクラスをすべてのクラス変数およびソース・ファイル内のステートメントによって収集された文(静的{})文合併順次コンパイラの静的ブロックを集め割り当て操作コンパイラでありますそれの後に変数に定義されているように、前の静的ステートメントブロックによって文のブロックで定義された静的変数へのアクセスのみを決定した順序で表示され、静的ステートメントブロックの前に割り当てることができますが、アクセスすることはできません。

  たとえば、次のコードは文句を言うでしょう。

  

 

   しかし、あなたはエラーではありません静的な静的コードブロック上のライン14にコードを置きます。コードの順序を変更しない、コードが削除されたり、ライン11は、与えられません。

  ②、<clinit>()コンストラクタクラスのメソッド(またはインスタンスコンストラクタ<初期化>()メソッド)が異なる、仮想保証サブクラス<初期化>に、親クラスのコンストラクタコールは表示されません()メソッドを実行する前に、親クラスの<init>()メソッドが完了しています。従って最初のクラスの仮想マシン<初期化>実行される()メソッドは、java.lang.Objectのなければなりません。

  ③、親が<clinit>()メソッドは、変数割当における親クラスブロック優先サブクラスで定義されたので、静的ステートメントを実行するため。

  クラスは、文の静的ブロックでない場合④、インタフェース用の<clinit>()メソッドは、必要ではないが、変数のない割り当てが存在しない場合、コンパイラは、このクラス<clinit>()メソッドを生成することができません。

  ⑤、インターフェースは、インターフェースとクラスは、<clinit>()メソッドが生成されるように、静的ステートメントブロックを使用することができないが、それでも変数割り当てが初期化されます。しかし、異なるインターフェイスとクラスが実行されたインタフェース<clinit>()メソッドは、親インタフェース<clinit>()メソッドを実行する必要はありません。インタフェースで定義された親の変数が使用された場合にのみ、親インタフェースが初期化されます。

  ⑥、インタフェースの実装クラスの初期化は、<clinit>()インターフェース方法と同様に実行されません。

  ⑦、<clinit>()メソッドのクラスを確保するための仮想機会が適切にロックされ、マルチスレッド環境で同期されます。複数のスレッドがクラスを初期化する場合は、唯一の<clinit>()メソッドのこのタイプを実行するために、1つのスレッドがあるだろう、他のスレッドは、アクティブなスレッドの実行<clinit>()メソッドが完了するまで待つ必要がブロックされています。非常に時間のかかる操作は<clinit>()メソッドのクラスに存在する場合、それは複数のプロセスの妨害を引き起こす可能性があります。

  たとえば、次のコード:

パッケージcom.yb.carton.controller。

/ ** 
 * YSOceanによって作成
 * / 
パブリック クラスClassLoadInitTest { 


    静的 クラスこんにちは{
         静的{
             場合){ 
                System.out.printlnは(にThread.currentThread()のgetName()。 + "INIT" );
                一方、){} 
            } 
        } 
    } 

    パブリック 静的 ボイドメイン(文字列[]引数){
         新しいスレッド(() - > {
            System.out.println(。にThread.currentThread()のgetName() + "開始" ); 
            こんにちは、H1 = 新しいこんにちは(); 
            System.out.println(にThread.currentThread()のgetName()。 + "上で実行" ); 
        })。開始(); 


        新しいスレッド(() - > { 
            System.out.printlnは(にThread.currentThread()のgetName()。 + "開始" ); 
            こんにちは、H2 = 新しいこんにちは(); 
            System.out.printlnは(にThread.currentThread()のgetName。 () + "上で実行" ); 
        })(開始)。
    } 

}
コードの表示

  結果は以下の通りであります:

  

 

   スレッド1は、<clinit>()メソッドの実装をつかんで、この方法は、無限ループで、スレッド2が待機ブロックします。

  それは、それをトリガされたときにクラスを初期化、その後、初期化クラスを知っていますか?JVMは、おそらく次のような状況を提供します。

  ①、仮想マシンが起動され、初期化ユーザーが指定したクラス。

  ②、新しいターゲットクラスのインスタンスを作成するための新しい命令、新しい初期指定されたターゲットクラスに直面したとき。

  ③、静的メソッド、クラスが配置されている初期化する静的メソッドを呼び出す命令に直面したとき。

  ④、クラスの命令には、静的フィールドにアクセスすることになると、静的フィールドが配置されて初期化します。

  ⑤、サブクラスは親クラスの初期化をトリガする初期化します。

  インタフェースはインタフェースのクラス初期化を達成するために、直接または間接的に、デフォルトのメソッドを定義する場合⑥、インターフェースは、初期化をトリガします。

  ⑦、クラスは、リフレクションAPIを使用して呼び出しを反映したとき、このクラスが初期化されます。

  ⑧、MethodHandleインスタンスの場合、元の呼び出し、MethodHandle点が配置されている初期化方法が挙げられます。

 

おすすめ

転載: www.cnblogs.com/ysocean/p/11427536.html