序文
シンプルな学習クラスファイルの構造上の記事では、この時間は、仮想マシンがクラスファイルをロードするために、および追加の処理負荷の後に何をすべきかを確認します
クラスファイルのロード処理
図に次のようにクラスファイルのローディング処理がコネクタを検証、製造および分析ステップ3に分割されている請求項3に接続された負荷と初期化ステップに分割することができ、全体的なプロセスです。
1.クラスの負荷条件
クラスファイルは、使用しなければならないときにのみロードされ、Java仮想マシンは、それが初期化されなければならない、前に最初に使用するクラスまたはインタフェースを提供します。ここでは積極的に活用の使用は、唯一の次のような状況では積極的に使用します。
- 新たなキーワードまたは反射、クローニング、デシリアライズとしてクラスのインスタンスを作成する場合
- すなわち、バイトコード命令を使用してクラスの静的メソッド呼び出し、invokestatic
- 静的フィールドは、使用またはputstatic命令と、(最終的な定数を除く)クラスまたはインタフェースを使用getstatic
- 反射法クラスパッケージjava.lang.reflectのを使用している場合
- 親クラスを初期化するために、サブクラスの初期化要求
- クラスのmainメソッドが含まれている仮想マシンを起動
上記の状況を積極的に活用され、初期化に加えて、残りは受動的に使用され、受動的な使用は、クラスが発生することはありません
積極的に容易に理解を使用して、以下の実施例は、受動メモリを使用して簡単に与えます
パブリック クラス親{ 静的{ するSystem.out.println(「親INIT」)。 } パブリック 静的 int型 V = 100 。 } パブリック クラス子供延び親{ 静的{ するSystem.out.println(「子INITを」)。 } } パブリック クラスTest1を{ 公共 静的 ボイドメイン(文字列[]引数){ のSystem.out.println(Child.v)。 } }
上記の次の出力操作コード
親のinit
100
それは子供を初期化されていないサブクラスのオブジェクトのTest1への直接アクセスが、しかし、親のみ初期化されますが、フィールドを使用する場合、我々は唯一の直接フィールドのクラスを定義するために、見ることができ、ここで子供ものの(初期化されます)それが初期化されていないが、それはロードされているが、初期化フェーズに入りませんでした
使用-XX:+ TraceClassLoadingこのコードを実行して、次のログを得ることができる(小さな部分だけがとら)
[ファイルからロードcom.xxxx.Parent:/ D:/ ideaWorkSpace / XXXX /標的/クラス/ ] [ファイルからロードcom.xxxx.Child: / D:/ ideaWorkSpace / XXXX /標的/クラス/ 】 親INIT 100
我々は2つのクラスがシステムにロードされている見ることができます
2.負荷クラス
クラスをロードする最初のクラスローディング段階です。クラスがロードされると、仮想マシンは、以下を実行します:
- ストリームのクラスを取得するには、クラスの完全な名前でバイナリデータ
- クラスデータ構造領域のバイナリデータストリーム解析方法
- 型を表すのjava.lang.Classクラスのインスタンスを作成します
バイナリデータクラスのクラス・ファイル・システムからのストリームなどがクラスファイルを抽出するためのzipファイルであってもよいが、ネットワークからロードされ、実行時に生成しても、いくつかのクラスのバイナリ情報であってもよいです。バイナリ情報を取得した後、Java仮想マシンは、最終的に、のjava.lang.Classのインスタンス、クラスの例では、タイプ・メタデータ・アクセス・インタフェースであるが、臨界反射データを達成するために、データを処理しました。
3.クラスを確認してください
システムにクラスローディングは、接続動作の開始後、最初のステップは、目的のロードバイトコードは、法的妥当と対応であることを確認するために、より複雑な検証手順、実質的に以下行われる必要性を確認し、検証を接続することです。
4.準備
ときにクラス検証することにより、仮想マシンは、準備の段階に入ります。この段階では、適切なメモリ空間に割り当てられたクラスの仮想マシン、および初期値を設定します。次の表の変数の各タイプの初期値
タイプ | デフォルトの初期値 |
int型 | 0 |
長いです | 0L |
ショート | (ショート)0 |
CHAR | \ U0000 |
ブーリアン | 偽 |
参照 | ヌル |
浮く | 0F |
ダブル | 0F |
デフォルトは0であるので、デフォルトはfalseのブールであるため、Javaは、実際にはブールのint型のため、int型をboolean型をサポートしていません。
一定のクラスフィールドがある場合、そのフィールドは、準備段階で正しい定数値を含むであろう。
5.解析クラス
第三のステップは、接続位相を解析することで、作業相が直接参照にシンボルクラス、インタフェース、フィールドおよびメソッド参照を解決することです。シンボリックリファレンス、一部のリテラルの参照にかかわらず、仮想マシンの内部データ構造とメモリレイアウトの。クラスのクラスファイルでは、定数プール内のシンボルの参照がたくさん。
INVOKEVIRTUAL#24 <javaの/ IO / PrintStream.priintln>
これは、のSystem.out.println()バイトコードであり、定数プール、以下の構造を表示および分析する定数プール24の使用は、見ることができることがわかります
24の定数プールは、検索のCONSTANT_Methodref参照関係#24に沿って、INVOKEVIRTUAL引用され、最終的にすべてのクラスへの参照を見つけ、NameAndTypeタイプは文字列に基づいています。したがって、リテラルな説明を参照することによりINVOKEVIRTUAL関数呼び出しが明らかとなっていると考えられます。これはシンボリック参照です。
実際にメソッドが呼び出されたときのみ、シンボリック参照は、十分ではありません実行し、システムが正確に位置特定方法を知っている必要があります。あなたはクラスのメソッドを呼び出すために必要がある場合の方法では、例えば、各メソッドのJava仮想マシンは、ちょうどこの方法は、上の表の方法でオフセットされていることを知って、テーブルの作成方法は、メソッドのすべてが記載されていますこれは、直接呼び出すことができます。動作を解析することによって、基準シンボル位置は、クラスのメソッドテーブル内のターゲットメソッドに変換することができます。
要約すると、それは、シンボリック参照を直接参照に変換される解決するために、クラス、フィールドまたはメソッドポインタまたはメモリ内のオフセットを与えることです。
6.初期化
初期化は、クラスローディングの最終段階です。前の手順は問題がない場合は、それがシステムに正常にロードできるクラスを表します。この時点で、クラスは、Javaバイトコードを実行するために開始されます。初期化メソッドの重要なタスクは、<clinit>のクラスの初期化フェーズを実行することです。方法<clinit>は自動的に静的メンバと静的割り当てステートメントブロック共生成され、コンパイラによって生成されます。
パブリック クラスSimpleDemo { パブリック 静的 int型の ID = 1 。 公共の 静的な int型の数。 静的{ 数 = 4 。 } }
コンパイルされたバイトコードとして、このクラスの上記の例、
パブリック クラスSimpleDemo { <ClassVersion = 52> <SOURCEFILE = SimpleDemo.java> パブリック静的int型のID。 公共の静的なint型の数。パブリック SimpleDemo(){ // <初期化> // ()V <localVar:インデックス= 0、名前= この、DESC = LSimpleDemo ;, SIG = ヌル、開始= L1、端= L2> L1 { aload0 // への参照自己 のJava /ラング/オブジェクトinvokespecial。<初期化> ()Vの リターン } L2 { } } 静的 { // <clinit> // ()V L1 { iconst_1で putstatic SimpleDemo.id:INT } L2 { iconst_4 putstatic SimpleDemo.number:INT } L3 { リターン } } }
世代の、<clinit>メソッド、クラスの静的な割り当てと文の静的ブロック、順次ID番号の割り当ての統合を参照してください。
クラスをロードする前に、先に述べたように、仮想マシンは常に親クラスのクラスをロードしようとするので、親クラス<clinit>メソッドは常に<clinit>サブクラスの前に呼び出され、それは、静的のサブクラスでありますブロックは、親クラスの後に行われます。
いないすべてのクラスは、クラスが何の割り当てを持っていない場合、コンパイラはクラスの<clinit>メソッドを生成しない静的な文ブロックが存在しない、<clinit>メソッドを持つことになります。
もう一つ重要な点は、仮想マシンのスレッドセーフ<clinit>メソッドは、複数のスレッドが同じクラスを初期化しようとすると、最初のスレッドの実行が成功した場合、1つのスレッドのみ、<clinit>メソッドを入力することができます実行したことですスレッドの後ろに移動するための方法を実行しません。
クラスが初期化されるときにも起因する<clinit>スレッドセーフロックされているので、それはマルチスレッド環境でのデッドロックにつながる可能性があり、このデッドロックを見つけることは困難です。ここでは、デッドロックの例です
クラスStaticA { 静的{ しようと{ のThread.sleep( 1000 ); } キャッチ(InterruptedExceptionある電子){ } 試みる{ Class.forNameの( "com.blog.test.StaticBを" )。 } キャッチ(ClassNotFoundExceptionが電子){ } のSystem.out.println( "com.blog.test.StaticA INIT成功" )。 } } クラスStaticB { 静的{ 試み{ のThread.sleep( 1000年)。 } キャッチ(InterruptedExceptionある電子){ } 試みる{ Class.forNameの( "com.blog.test.StaticAを" )。 } キャッチ(ClassNotFoundExceptionが電子){ } のSystem.out.println( "com.blog.test.StaticB INIT成功" )。 } } パブリック クラス StaticDeadLockMainは延びスレッド{ プライベート チャーフラグ。 公共 StaticDeadLockMain(チャーフラグ){ この .flag = フラグ。 この .setName( "スレッド" +フラグ)。 } @Override 公共 ボイドラン(){ 試みる{ Class.forNameの( "com.blog.test.Static" + フラグ)。 } キャッチ(ClassNotFoundExceptionが電子){ e.printStackTrace(); } のSystem.out.println(のgetName() + "上" )。 } パブリック 静的 ボイドメイン(文字列[]引数){ StaticDeadLockMain loadA = 新しい StaticDeadLockMain( 'A' )。 loadA.start(); StaticDeadLockMain LOADB = 新しいですStaticDeadLockMain( 'B' )。 loadB.start(); } }
上記のコードは、単に別のクラスは、それぞれ初期化するためにリソースをつかむ形成され、静的クラス初期化コードブロックです。mainメソッドを実行するコードは、mainメソッドが停止されていないれた、参照jstack以下は出力、スタック情報が存在しません
デッドロックを決定するのに十分な情報を持っていなかった情報をスタックが、デッドロックが存在しないので、私たちはクラスを初期化するとき、このデッドロック状態の余分な注意します。