クラスローダー
これは、コンパイルされたバイトコードを JVM に提供するために使用され、通常は拡張子 のファイルに保存されます.class
が、ネットワーク経由でダウンロードしたり、アプリケーション自体によって生成したりするなど、他のソースから取得することもできます。 Java SE 仕様によれば、JVM でコードを実行するには、次の 3 つの手順を完了する必要があります。
-
リソースからバイトコードをロードし、クラスのインスタンスを作成する
Class
これには、以前にロードされたクラスの中から要求されたクラスを検索すること、
Class
ロード用のバイトコードを取得してその正しさを確認すること、クラスのインスタンスを作成すること (実行時に操作するため)、親クラスをロードすることが含まれます。親クラスとインターフェイスがロードされていない場合、問題のクラスはロードされていないとみなされます。 -
バインディング (またはリンク)
仕様によれば、このステージはさらに 3 つのステージに分かれています。
- 検証: 受信したバイトコードの正当性がチェックされます。
- 準備、静的フィールドに RAM を割り当て、それらをデフォルト値で初期化します (この場合、明示的な初期化があれば、初期化段階ですでに行われます)。
- Resolution、型、フィールド、メソッドのシンボリック リンクの解決。
-
受信したオブジェクトを初期化しています
ここでは、前の段落とは異なり、何が起こるべきかすべてが明らかであるように見えます。もちろん、これがどのようにして起こるのかを正確に理解することは興味深いでしょう。
- クラスはリンクされる前に完全にロードされている必要があります。
- クラスは初期化する前に完全にテストして準備する必要があります。
- リンク解決エラーは、リンク段階で検出された場合でも、プログラムの実行中に発生します。
Java ローダーの種類
Java には 3 つの標準ローダーがあり、それぞれが特定の場所からクラスをロードします。-
Bootstrapは基本的なローダーであり、Primordial ClassLoader とも呼ばれます。
rt.jar アーカイブから標準の JDK クラスをロードします
-
拡張クラスローダー- 拡張ローダー。
拡張クラスをロードします。拡張クラスはデフォルトでは jre/lib/ext ディレクトリにありますが、java.ext.dirs システム プロパティによって設定できます。
-
システム クラスローダー– システム ローダー。
CLASSPATH 環境変数で定義されたアプリケーション クラスをロードします。
抽象クラス ClassLoader
基本ローダーを除く各ローダーは、抽象クラスの子孫ですjava.lang.ClassLoader
。たとえば、拡張ローダーの実装は クラス でsun.misc.Launcher$ExtClassLoader
、システム ローダーは ですsun.misc.Launcher$AppClassLoader
。ベース ローダーはネイティブであり、その実装は JVM に含まれています。拡張するクラスは、java.lang.ClassLoader
ブラックジャックやこれらと同じクラスをロードする独自の方法を提供できます。これを行うには、対応するメソッドを再定義する必要がありますが、現時点では表面的にしか検討できません。この問題については詳しく理解できませんでした。どうぞ:
package java.lang;
public abstract class ClassLoader {
public Class<?> loadClass(String name);
protected Class<?> loadClass(String name, boolean resolve);
protected final Class<?> findLoadedClass(String name);
public final ClassLoader getParent();
protected Class<?> findClass(String name);
protected final void resolveClass(Class<?> c);
}
loadClass(String name)
数少ないパブリック メソッドの 1 つで、クラスをロードするためのエントリ ポイントです。loadClass(String name, boolean resolve)
その実装は要約すると、オーバーライドする必要がある別の保護されたメソッドを呼び出すことになります。この保護されたメソッドの Javadoc を見ると、次のようなことがわかります。2 つのパラメーターが入力として提供されています。1 つは、ロードする必要があるクラスのバイナリ名 (または完全修飾クラス名) です。クラス名はすべてのパッケージのリストで指定されます。2 番目のパラメーターは、シンボリック リンクの解決が必要かどうかを決定するフラグです。デフォルトではfalseで、遅延クラスローディングが使用されることを意味します。さらに、ドキュメントによると、メソッドのデフォルト実装では、findLoadedClass(String name)
クラスが以前にロードされているかどうかを確認し、ロードされている場合はこのクラスへの参照を返す呼び出しが行われます。それ以外の場合は、親ローダーのクラスロードメソッドが呼び出されます。どのローダーもロードされたクラスを見つけることができなかった場合、各ローダーは逆の順序で、そのクラスを見つけてロードし、 をオーバーライドしようとしますfindClass(String name)
。これについては、「クラスローディングスキーム」の章で詳しく説明します。最後に、クラスがロードされた後、解決フラグに応じて、シンボリック リンクを介してクラスをロードするかどうかが決定されます。わかりやすい例は、クラスの読み込みステージ中に解決ステージを呼び出すことができることです。したがって、クラスを拡張しClassLoader
、そのメソッドをオーバーライドすることにより、カスタム ローダーは仮想マシンにバイトコードを配信するための独自のロジックを実装できます。Java は、「現在の」クラス ローダーの概念もサポートしています。現在のローダーは、現在実行中のクラスをロードしたローダーです。各クラスはどのローダーでロードされたかを認識しており、その を呼び出すことでこの情報を取得できますString.class.getClassLoader()
。すべてのアプリケーション クラスでは、通常、「現在の」ローダーはシステム ローダーです。
クラスローディングの 3 つの原則
-
代表団
クラスをロードするリクエストは親ローダーに渡され、親ローダーがクラスを見つけてロードできなかった場合にのみ、クラス自体のロードが試行されます。このアプローチにより、基本ローダーにできるだけ近いローダーを使用してクラスをロードできます。これにより、クラスの可視性が最大限に高まります。各ローダーは、ロードされたクラスの記録を保持し、それらをキャッシュに置きます。これらのクラスのセットはスコープと呼ばれます。
-
可視性
ローダーは「その」クラスと「親」のクラスだけを認識し、「子」によってロードされたクラスについては知りません。
-
独自性
クラスは 1 回だけロードできます。委任メカニズムにより、クラスのロードを開始するローダーが、以前に JVM にロードされたクラスをオーバーロードしないことが保証されます。
クラスローディングスキーム
クラスをロードする呼び出しが発生すると、このクラスは現在のローダーのすでにロードされているクラスのキャッシュ内で検索されます。目的のクラスが以前にロードされていない場合、委任の原則により、階層の 1 つ上のレベルにある親ローダーに制御が移されます。親ローダーも、キャッシュ内で目的のクラスを見つけようとします。クラスがすでにロードされており、ローダーがその場所を知っている場合は、Class
そのクラスのオブジェクトが返されます。そうでない場合は、ベース ブートローダーに到達するまで検索が続行されます。ベースローダーが必要なクラスに関する情報を持っていない場合 (つまり、まだロードされていない場合)、このクラスのバイトコードは、指定されたローダーが知っているクラスの場所で検索されます。ロードされると、制御は子ローダーに戻り、子ローダーは既知のソースからロードしようとします。上で述べたように、ベース ローダーのクラスの場所は rt.jar ライブラリ、拡張ローダーの場合は jre/lib/ext 拡張子を持つディレクトリ、システムのクラスの場所は CLASSPATH、ユーザーのクラスの場所は別のものにすることができます。 。したがって、クラスのロードの進行は逆方向、つまりルート ローダーから現在のローダーへ進みます。クラスのバイトコードが見つかると、クラスが JVM にロードされ、その型のインスタンスが取得されますClass
。簡単にわかるように、説明した読み込みスキームは、上記のメソッドの実装に似ていますloadClass(String name)
。以下にこの図を示します。
結論として
言語学習の最初のステップでは、Java でクラスがどのようにロードされるかを特に理解する必要はありませんが、これらの基本原則を知っていれば、 や などのエラーが発生したときに絶望することを避けることができClassNotFoundException
ますNoClassDefFoundError
。まあ、少なくとも問題の根本が何であるかを大まかには理解できます。ClassNotFoundException
したがって、プログラムの実行中にクラスが動的にロードされるとき、ローダーがキャッシュ内またはクラスパスに沿って必要なクラスを見つけられない場合、例外が発生します。ただし、このエラーNoClassDefFoundError
はより重大で、必要なクラスがコンパイル中に利用可能であったが、プログラムの実行中に表示されなかった場合に発生します。これは、プログラムが使用するライブラリをインクルードするのを忘れた場合に発生する可能性があります。そうですね、仕事で使用するツールの構造原理を理解するという事実自体 (ツールの奥深くまで明確かつ詳細に浸る必要はありません) によって、このメカニズムの内部で発生するプロセスの理解がさらに明確になります。順番に、このツールを自信を持って使用することにつながります。
GO TO FULL VERSION