「Java仮想マシンの深い理解」の学習 - クラスローディング機構
1.概要
Java型を形成し、メモリ内にクラスファイルからロードされたクラスを記述し、データを確認し、初期化を解析し変換する仮想マシンのデータは、仮想マシンのクラスローディング機構である仮想マシンとして使用することができます。
2.クラスのロード時間
2.1クラスのライフサイクル:
負荷 - >接続(ベリファイ - >準備 - >分析) - >初期化 - >使用 - >アン
その中で、5段階のロード、検証、準備、初期化およびアンロードシーケンスが決定され、クラスのロード処理がこの順序で段階的に開始し、してはならない必然分析相:いくつかのケースではそれが初期化することができますJava言語ランタイムバインディングをサポートすることです再起動相、後。
2.2初期化クラス
そして、クラスの唯一の4例は、すぐに「初期化」する必要があります。
クラスが初期化されていない場合は、この4バイトコード命令は、初期化がトリガするのに必要とされる新しい、getstatic、またはputstatic invokestaticに遭遇し、その
シーン:
- オブジェクトをインスタンス化する新しいキーワードを使用します
- 読み取りまたはクラスの静的フィールドを設定し(最終修正を、コンパイラが静的フィールド以外の定数プールに結果となっています)
- 授業時間の静的メソッドを呼び出します
使用し
java.lang.reflect
たパッケージのアプローチは、呼び出すためにクラスを反映するとき、クラスが初期化されていない場合、あなたは彼らの初期化をトリガーする必要がありますクラスを初期化するとき、あなたが親クラスが初期化されていない見つけた場合、あなたは親クラスの初期化をトリガーする必要があります
仮想マシンが起動すると、ユーザは、(含ま実行するメインクラスを指定する必要があり
main()
、このマスタークラスを初期化するために、そのクラスのメソッド)仮想マシンを
3.クラスのロード処理
3.1ロード
ロード・フェーズでは、仮想マシンは、3つのことを実行する必要があります。
- クラスの完全修飾名で定義されたバイナリバイトストリームを取得するには
- これは、ランタイムデータ構造領域法に静的記憶構造のバイトストリームを表します
- このクラスのJavaヒープの代表生成する
java.lang.Class
データ入力領域にアクセスするための方法として、オブジェクトを
3.2検証
私たちは、4段階の検証プロセスを完了します。
- ファイル形式の検証
- メタデータの検証
- バイトコード検証
- シンボリック参照の検証
3.3製造
準備フェーズは、正式にクラス変数にメモリを割り当て、ステージクラス変数の初期値に設定され、このメモリはであろう方式エリアに割り当てられました。
いくつかの点に注意してください。
この時間は、専用メモリ割り当て含むクラス変数(静的変数を変更する)、およびインスタンス変数が含まれていないオブジェクトがインスタンス化されるとき、インスタンス変数は、オブジェクトに沿って割り当てられるJavaヒープを(初期化フェーズ)において。
ここで、初期値は「通常」のデータ型でゼロ値の値はJavaコードで明示的に与えられているのではなく、(例えば、0,0L、ヌル、偽、等)。
例えば:
// 变量 value 在准备阶段过后的初始值为 0 而不是 123 public static int value = 123;
特別な場合:そこフィールドに属性テーブル一定値クラス属性フィールドは、(最終すなわち、定数)であり、準備段階で変数の値を一定値プロパティで指定された値に初期化されている場合。
例えば:
// 编译时 Javac 将会为 value 生成 ConstantValue 属性,在准备阶段虚拟机就会根据 ConstantValue 的设置将 value 赋值为 123 public static final int value = 123;
解析された3.4
ステージを解析すると、直接参照を交換するプロセスへのシンボリック参照の定数プールへの仮想マシンです。
コンセプト注:
- 符号:参照に記載されるシンボルのシンボル基準目標セット、リテラルシンボルは、任意の形態であってもよく、限り目標を使用するように明確に位置付けることができます。シンボル参照は、仮想マシンを達成するためのメモリレイアウトとは何の関係もない、目標は必ずしもメモリにロードされた基準ではありません。
- 直接参照:直接参照は、オブジェクトへの直接ポインタ、相対オフセットまたは間接的にターゲットを処理するために標的化され得ます。直接参照は、仮想マシンに関連付けられたメモリレイアウトを実現することで、異なるシンボルを使用して仮想マシンインスタンスに翻訳引用は、一般的に直接同じ参照ありません。あなたがその目標基準への直接参照を持っている場合は、すでにメモリ内に存在している必要があります。
3.5初期化
クラス初期化フェーズは、クラスローディング上記クラス負荷(のプロセスの最後のステップで準備フェーズ参加の外部からのクラスローダを定義することができ、ユーザ・アプリケーションは、残りの動作は完全に仮想マシンと制御によって支配される負荷相の間を除いて、)。初期化フェーズに、実際にJavaコードで定義されたクラスを開始しました。
初期化フェーズは、クラスのコンストラクタに行われる<clinit>()
方法の手順を:
準備フェーズでは、クラス変数は、システム要件一旦初期値が代入されており、初期化フェーズでは、クラス変数およびそのようなプログラムによって開発された主観的プログラマー計画に基づいて、クラスのメンバ変数として他のリソースを(初期化する - クラスの外部変数に加えて変数は)クラスのメンバ変数です。
心に留めておくべきいくつかの概念:
<clinit>()
操作の方法は、自動的にすべてのクラスのクラス変数(収集代入文ブロックと静的コンパイラであるstatic{}
(ブロック)が生成文をマージ優先{}静的があります)そのサブクラスを確保するために、仮想機会
<clinit>()
実行方法の前に、親クラスの<clinit>()
メソッドがすでに実装されています。そのため、最初の仮想マシンを実行する<clinit>()
確かにクラスメソッドをjava.lang.Object
。親クラスとして
<clinit>()
、親クラスで定義されているステートメントの静的ブロックを意味可変サブクラスの割り当てよりも優先される最初の実行する方法。たとえば、次のコード値演算結果フィールドBは、2の代わりに1であろう。
static class Parent { public static int A = 1; static { A = 2; } } static class Sub extends Parent { public static int B = A; } public static void main(String[] args) { System.out.println(Sub.B); }
<clinit>()
クラスまたはインタフェースのための方法は、次に、コンパイラは、クラスを生成することができない、クラスが文の静的ブロックでない場合、変数のない割り当てが存在しない、必要ではない<clinit>()
方法を。インターフェイスブロック静的ステートメントを使用することができないが、それでも変数割り当てが初期化され、したがって、同じクラスインターフェースを生成
<clinit>()
する方法を。
4.クラスローダ
二つのクラスの4.1。比較するかどうか「等しいです」
のみ、これらの二つのカテゴリーでは、これら2つのクラスがいる限り、彼らは別のクラスローダであるように、同じファイルからクラスで、この2つのクラスということも、そうでない場合は、同じクラスローダが理にかなっているという前提の下にロードされ、それは確かに同じではありません。
4.2クラスローダ
別のクラスローダ(Java仮想マシンの角度)
- クラスローダ(C ++の実装)を起動します。仮想マシン自体の一部
- 他のすべてのクラスローダ(Java実装):外部の仮想マシンとは独立して、抽象クラスから継承されました
java.lang.ClassLoader
別のクラスローダ(Java開発者の視点)
ブートクラスローダ:上記と一致
責任はに格納される
<JAVA_HOME>\lib
ディレクトリ、または-Xbootclasspath
パラメータは、パスを指定し、仮想マシンの識別ライブラリは、仮想マシンのメモリにロードされます。スタートクラスローダは、直接Javaプログラムを参照することはできません。拡張クラスローダ:
sun.misc.Launcher$ExtClassLoader
実現ロードの責任
<JAVA_HOME>\lin\ext
ディレクトリを、またはによってjava.ext.dirs
すべてのライブラリ、現像システム変数指定されたパスは、直接拡張クラスローダを使用することができますアプリケーションクラスローダ:
sun.misc.Launcher$AppClassLoader
インプリメンテーションクラスローダがあるので、メソッドの戻り値が、それは一般的にシステムクラスローダと呼ばれています。それは、これがデフォルトのクラスローダでのプログラムのユーザー・パスプログラムは、一般的には、そのカスタムクラスローダされていないかどうかを開発者が直接、このクラスローダを使用することができます(クラスパス)指定されたライブラリをロードする責任があるさデバイス。
ClassLoader
getSystemClassLoader()
4.3親委任モデル(重要)
上:
構造:
親委任モデルに加えて、クラスローダの残りの部分は、自身の親クラスローダを持っている必要があり、トップレベルのブートローダーのクラスが必要です。ここではクラスローダ間の親子関係は、一般的な関係を継承するために達成しますが、親ローダーとコード多重関係の組み合わせを使用していません。
作業プロセス:
クラスローダは、クラスが要求を受け取っロードする場合、とてもすべて、最初のクラスをロードするために彼の最初の試みであるが、クラスローダの各レベルを完了するために、親クラスローダにこの要求を委任することは真実ではありませんでしょう最後のロード要求は(その検索で目的のカテゴリを見つけられませんでした)親クラスローダが、彼らは要求を完了することはできませんフィードバックをロードするときにのみ、ブートクラスローダのトップに送信する必要があり、サブローダーがしようとします自身がロードします。
原則
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
// 首先,检查请求的类是否已经被加载过了
Class c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNUll(name);
}
} catch (ClassNotFoundException e) {
// 如果父类加载器抛出 ClassNotFoundException
// 则说明父类加载器无法完成加载请求
}
if (c == null) {
// 在父类加载器无法加载的时候
// 再调用本身的 findClass 方法进行类加载
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
ローダがロードされない場合は、最初のチェックがすでにロードされていた、親を呼び出すloadClass()
親ローダは、親クラスローダとして、デフォルトのブートローダーを空にした場合、メソッドを。親が読み込めない場合は、スローClassNotFoundException
例外の後、そして自分自身を呼び出すfindClass()
ロードする方法を。
Javaは両親の委任モデルの例を満たしていません
基本クラスは、ユーザーコードにコールバックする必要があります
クラスローダによってコンテキストクラスローダ(スレッドコンテキストクラスローダ)への参照は、
java.lang.Thread
クラスのsetContextClassLoaser()
スレッドが作成されている場合、設定の方法が設定されていない、それは親スレッドから継承されます。両方のグローバルアプリケーションの場合デフォルトのクラスローダーは、アプリケーションクラスローダで、設定されませんでした。親クラスローダクラスローダ要求は、サブクラスのロード操作を完了するので、逆に実際にクラスローダを使用している、親は委任モデルを満たしていません。
以下のような:JDBC
ダイナミックプログラム
あるいはホットコード、熱モジュールの展開。(実際には、モジュール式の操作)
私たちは、「OSGiの」呼んこのモジュラースタンダードモデル。(クラスローダのメカニズムは、カスタム実装します)
- この規格では、各プログラムモジュール(バンドル)は、独自のクラスローダを有する場合バンドル交換する必要があり、バンドルは、置換コードを行うためにもグレードの熱負荷を交換まとめます。
OSGi環境では、ツリーは、クラスローダ委譲モデルは、両親の結果が、ネットワーク構造のさらなる発展ではなくなりました。