1.クラスの読み込みプロセス
1.1ロード
ロードとは、クラスのクラスファイルをメモリに読み込み、そのためのjava.lang.Classオブジェクトを作成することです。つまり、プログラムでクラスが使用されると、システムはそのクラスのjava.lang.Classオブジェクトを作成します。
ローカルシステム/ネットワーク/ JARパッケージ/ Javaソースファイル内のクラスファイル、動的コンパイルなど、クラスのソースは多数あります。
クラスのロードは、JVMが提供するクラスローダーによって完了します。クラスローダー
については後で説明します。
1.2接続
クラスがロードされると、システムはそれに対応するClassオブジェクトを生成し、次に接続フェーズに入ります。接続フェーズは、クラスのバイナリデータをJREにマージする役割を果たします。クラス接続は、次の3つの段階に分けることができます。
1.2.1検証
検証フェーズは、ロードされたクラスが正しい内部構造を持ち、他のクラスと整合性があるかどうかを検証するために使用されます
- ファイル形式の検証:バイトストリームがクラスファイル形式の仕様に準拠しており、現在の仮想マシンでロードおよび処理できることを確認します
- メタデータ検証:バイトコードによって記述された情報のセマンティック分析。Java言語の文法仕様に準拠しているかどうかを分析します。
- バイトコード検証:最も重要な検証リンクは、データフローと制御を分析して、セマンティクスが正当で論理的であるかどうかを判断することです。主にメタデータ検証後のメソッド本体の検証に使用します。クラスメソッドが実行時に有害でないことを確認してください。
- シンボリック参照の検証:シンボリック参照が直接参照に変換されると、主にアクセスのタイプや参照に関連するその他のケースを判別するために、主に参照がアクセスされることを確認するために、第3の解析段階に拡張されます。クラスなどのアクセス不能の問題です。
1.2.2準備
クラス準備フェーズでは、クラスの静的変数にメモリを割り当て、デフォルトの初期値を設定します。非静的変数はメモリを割り当てません。
1.2.3分析
クラスのバイナリデータ内のシンボリック参照を直接参照に置き換えます。
- シンボル参照:シンボル参照は、参照されるターゲットを説明するための一連のシンボルです。シンボルは、競合がなく、位置を特定できる限り、任意のリテラル形式にすることができます。レイアウトはメモリとは何の関係もありません。
- 直接参照:直接配置できるターゲット、オフセット、またはハンドルへのポインタです。参照はメモリ内のレイアウトに関連しており、ロードする必要があります。
- 例:たとえば、個人のID番号123456879(記号の参照はプレースホルダーの意味でもあります)を取得した場合、貴重な情報を取得することはできませんが、公安局に問い合わせて確認すると、その個人のIDを見つけることができます。正確な自宅住所(直接見積もり)
1.3初期化
初期化は、クラスの静的変数に正しい初期値を割り当てることです
クラスに次のステートメントがある場合:private static int a = 10、その実行プロセスは次のようになります。
- まず、バイトコードファイルがメモリにロードされ、次にリンク検証ステップが実行されます。検証に合格した後、準備段階
- 変数aは静的であるため、メモリをaに割り当てます。この場合、aはint型0のデフォルトの初期値に等しくなります。つまり、a = 0
- 次に、解析と初期化に関しては、aの実際の値10がaに割り当てられ、この時点でa = 10になります。
1.4クラスの読み込みのタイミング
- 新しいオブジェクトであるクラスのインスタンスを作成します
- 特定のクラスまたはインターフェイスの静的変数にアクセスするか、静的変数に値を割り当てます
- クラスの静的メソッドを呼び出す
- 反射(Class.forName(“ com.chen.demo”))
- クラスのサブクラスを初期化します(サブクラスの親クラスが最初に初期化されます)
- JVMの起動時にマークされたスタートアップクラス、つまり同じファイル名とクラス名を持つクラス
さらに、次の状況を具体的に指摘する必要があります。
最終的な静的変数の場合、コンパイル時に変数の値を決定できる場合、この変数は「マクロ変数」と同等です。Javaコンパイラは、コンパイル時にこの変数が表示される場所をその値に直接置き換えるため、プログラムが静的変数を使用している場合でも、クラスの初期化は行われません。逆に、コンパイル時に最終的な静的フィールドの値を決定できない場合は、実行時に変数の値を決定する必要があります。このクラスを介して静的変数にアクセスすると、クラスが初期化されます。
2.クラスローダー
クラスローダーはすべてのクラスのロードを担当し、メモリにロードされたすべてのクラスのjava.lang.Classインスタンスオブジェクトを生成します。JVMの場合と同様にクラスがロードされると、同じクラスが再度ロードされることはありません。
クラスの唯一の兆候:
类名.包名.类加载器名
2.1ルートクラスローダー
- Javaのコアクラスをロードするために使用され、ネイティブコードで実装されます
- java.lang.ClassLoaderから継承しません
(负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++实现,不是ClassLoader子类)
- ブートクラスローダーには仮想マシンのローカル実装の詳細が含まれるため、開発者はスタートアップクラスローダーへの参照を直接取得できません。したがって、参照による直接操作は許可されません。
2.2拡張クラスローダー
- JREの拡張ディレクトリをロードする責任があります
- lib / ext内のJARパッケージのクラスまたはjava.ext.dirsシステムプロパティで指定されたディレクトリ。
- Java言語で実装されているため、親クラスローダーはnullです。
2.3システムクラスローダー
と呼ば系统(or 应用)类加载器
れ、Javaコマンドから-classpathオプション、java.class.pathシステムプロパティ、またはJVMの起動時に変数で指定されたJARパッケージとクラスパスのCLASSPATHをロードします。
程序可以通过ClassLoader的静态方法getSystemClassLoader()来获取系统类加载器。
指定されていない場合、ユーザー定義のクラスローダーはこのタイプのローダーを親ローダーとして使用します。これはJava言語で実装されており、親クラスローダーはExtClassLoaderです。
2.4クラスローダーがクラスをロードするための手順
-
このクラスがロードされているかどうか、つまり、このクラスがバッファにあるかどうかを確認し、対応する
java.lang.Class
オブジェクトを返します。それ以外の場合は、手順2に進みます。 -
親クラスローダーはありません親クラス
or
はルートクラスローダーor
ですそれ自体がルートクラスローダーです。ステップ4にスキップします。親クラスローダーが存在する場合は、手順3に進みます。
-
親クラスローダーを使用してターゲットクラスをロードするように要求します。ロードが成功した場合は、手順8にスキップし、それ以外の場合は手順5に進みます。
-
ルートクラスローダーを使用してターゲットクラスをロードするように要求します。ロードが成功した場合はオブジェクトを返し、そうでない場合は例外をスローします。
-
現在のクラスローダーは、クラスファイルを見つけようとします。見つかった場合は、手順6を実行し、見つからない場合は、手順7を実行します。
-
ファイルからクラスをロードし、成功したらステップ8にスキップします。
-
ClassNotFountException
例外をスローします。 -
対応する
java.lang.Class
オブジェクトを返します。
- バッファにこのクラスがあるかどうかを判断して返し、親クラスローダーがあるかどうかを判断します
- 親クラスが存在する場合は、親クラスローダーを使用してロードします。ロードが成功した場合は、jlcオブジェクトを返します。それ以外の場合は、現在のクラスローダーを使用してクラスファイルを見つけ、見つかった場合はロードしてから、正常にロードします。 CNFE例外をスローします
- ルートクラスローダーの場合:ロードが成功した場合はjlcオブジェクトが返され、それ以外の場合はCNFE例外がスローされます。
3.クラスローディングメカニズム
3.1全責任
クラスローダーがクラスのロードを担当する場合、別のクラスローダーを使用してクラスをロードしない限り、クラスが依存し、参照する他のクラスもクラスローダーによってロードされます。
3.2親の委任
いわゆる親委任とは、親クラスローダーが最初にクラスをロードしようとし、親クラスローダーがクラスをロードできない場合にのみ、自身のクラスパスからクラスをロードしようとすることです。
素人の言葉で言えば、特定のクラスローダーがクラスのロード要求を受信すると、最初にロードタスクを親ローダーに委任し、次に再帰的に委任します。親ローダーがクラスのロードタスクを完了できる場合は、正常に戻ります。親のみです。ローダーがこのロードタスクを完了できない場合、ローダーはそれ自体をロードします。
親委任メカニズムの動作原理:クラスローダーがクラスロード要求を受信した場合、最初にロードするのではなく、実行のために親クラスのローダーに要求を委任します。親クラスローダーにまだ親がある場合クラスローダーはさらに上向きに再帰的に委任します。リクエストは最終的に最上位のスタートアップクラスローダーに到達します。親クラスローダーがクラスロードタスクを完了できる場合は正常に戻ります。親クラスローダーがロードタスクを完了できない場合は、 childローダーはそれ自体をロードしようとします。これは親の委任モードです。即每个儿子都很懒,每次有活就丢给父亲去干,直到父亲说这件事我也干不了时,儿子自己才想办法去完成
親の委任メカニズムの利点:
可避免类重复加载
:Javaクラスとそのクラスローダーは優先度の高い階層関係にあります。この階層レベルにより、クラスの繰り返しのロードを回避できます。父親がすでにクラスをロードしている場合は、サブClassLoaderは必要ありません。1回ロード安全
:JavaコアAPIで定義された型は、自由に置き換えられません。名前付きjava.lang.Integer
クラスがネットワークを介して渡され、親委任モードを介してスタートアップクラスローダーに渡されると仮定すると、スタートアップクラスローダーは、でこの名前のクラスを検索します。コアJavaAPIが見つかり、このクラスがロードされ、ネットワークからリロードされませんjava.lang.Integer
。代わりに、ロードされたInteger.classが直接返されるため、コアAPIライブラリが自由に改ざんされるのを防ぐことができます。
3.3キャッシュメカニズム
キャッシュメカニズムにより、ロードされたすべてのクラスが確実にキャッシュされます。
プログラムでクラスを使用する必要がある場合、クラスローダーは最初にキャッシュ領域でクラスを検索し、システムはクラスオブジェクトがキャッシュされない場合にのみ検索します。キャッシュ領域に存在します。このクラスに対応するバイナリデータを読み取り、Classオブジェクトに変換して、バッファに保存します。
这就是为什么修改了Class后,必须重新启动JVM,程序所做的修改才会生效的原因。