この記事では、Java学習におけるJvmクラスの読み込みの仕組みを紹介します。困っている友人は参考にしていただければ幸いです。
仮想マシンはクラス ファイル (バイナリ バイト ストリーム) をメモリにロードし、データを検証、変換、解析、初期化し、最終的に次のようなファイルを形成します。 Java タイプでは、この一連のプロセスがクラスのロード メカニズムとして直接使用されます。
クラスは、仮想マシンによるメモリへのロードから開始され、メモリからアンロードされるまでの ライフ サイクル 全体には次のものが含まれます。 読み込み - - 検証 - 準備 - 解析 - 初期化 - 使用 - アンインストール この 7 段階です。検証、準備、解析の 3 つの部分をまとめて接続と呼びます。
ライフサイクル図は次のとおりです。
ロード、検証、準備、初期化、アンロードの 5 つのフェーズの順序が決定され、クラスのロード プロセスは、この順序で開始する必要があります。解析フェーズは必ずしもこの順序で開始する必要はありません。場合によっては、初期化後に開始することもできます。これは、Java 言語の動的バインディングをサポートするためでもあります。
new、getstatic、putstatic、invokestaticの4つの命令に遭遇した場合、クラスが初期化されていない場合は初期化(これら 4 つの命令が動作する最も一般的なシナリオは、オブジェクトの新しいインスタンス化、クラスの静的フィールドの読み取りまたは設定 (最終的な変更または定数プールに入れられた静的フィールドを除く)、static の呼び出しです。
リフレクションを使用する場合
クラスを初期化するとき、その親クラスがまだ初期化されていない場合は、次のことを行う必要があります。最初に親クラスの初期化をトリガーします
仮想マシンの起動時に、実行するメイン クラス (仮想マシンが初期化されるメイン メソッドを含むクラス) を指定する必要があります。このクラスを最初に使用します
#3。クラス読み込みプロセス
#- Loading##。このクラスを定義するバイナリ バイト ストリームを取得します。クラスの完全修飾名
このバイト ストリームで表される静的ストレージ構造をメソッド領域の実行時データ構造に変換します
Generateメモリ内のこの型の表現 java.lang.Object オブジェクトは、メソッド領域でこのクラスのさまざまなデータへのアクセス ポイントとして機能します。
クラス読み込みの他の段階と比較すると、ロード段階 (正確には、クラスのバイナリ バイト ストリームを取得するロード段階のアクション) は、開発者が最も制御できる段階です。ロードフェーズは、システムが提供するブートクラスローダーを使用して完了することも、開発者のカスタムクラスローダー(つまり、クラスローダーのloadClass()メソッドをオーバーライドすること)によって完了することもできるためです。
ロードフェーズの一部と接続フェーズが交互に行われており、ロードが完了するまで検証などの操作を行うことができません。ロード中に挟まれたこれらのアクションは引き続き接続フェーズに属し、これら 2 つのフェーズの開始時間は依然として固定シーケンスを維持します。
-検証
検証は、ロードされたバイナリ バイト ストリームに含まれる情報が仮想マシンの仕様に準拠していることを確認するための接続の最初のステップです。
: バイト ストリームがクラス ファイル形式の仕様に準拠しているかどうかを検証します。たとえば、マジックナンバー 0xCAFEBABE で始まるかどうか、メジャー バージョン番号とマイナー バージョン番号が現在の仮想マシンの処理範囲内にあるかどうか、定数プール内の定数にサポートされていない型があるかどうかなどです。
メタデータ検証: バイトコードで記述された情報に対してセマンティック分析を実行します。例: このクラスには親クラスがあるかどうか、また親クラスを正しく継承しているかどうか。
バイトコード検証: データ フローと制御フローの分析を通じて、プログラムのセマンティクスが正当かつ論理的であることを判断します (端的に言うと、クラスのメソッド本体を分析して、この方法が実行時に仮想マシンに害を及ぼさないことを確認してください)。
シンボル参照の検証: 解析アクションが正常に実行できることを確認します。
検証フェーズは非常に重要ですが、必ずしも必要なフェーズではありません (プログラムの実行時間には影響しないため)。実行中のすべてのコードが繰り返し使用され検証されている場合は、実装フェーズ中に -Xverify:none パラメーターを使用して検証をオフにすることができます。
クラス変数に正式にメモリを割り当て、クラス変数の初期値を設定します。これらの変数によって使用されるメモリはメソッド領域に割り当てられます。
注:
現時点では、静的変数のみが割り当てられ、インスタンス変数はオブジェクト インスタンスと一緒に割り当てられません。 Java ヒープ内
#初期値は通常、データ型のゼロ値です。静的変数 public static int value = 123; を定義すると、準備段階では value の初期値は 123 ではなく 0 になります。
#final によって変更された変数は、準備段階で属性によって指定された値に初期化されます。 例: public static Final int value = 123; の場合、準備フェーズの value の初期値は 123 です。
解析フェーズは、仮想マシンが定数プール内のシンボル参照を直接参照に置き換えるプロセスです。 。解析アクションは主に、クラスまたはインターフェイス、フィールド、クラス メソッド、インターフェイス メソッド、メソッド タイプ、メソッド ハンドル、および呼び出しサイト修飾子の 7 種類のシンボル参照に対して実行されます。
シンボル参照: シンボルのセットを使用して、参照のターゲットを記述します。シンボルには、任意の形式のリテラルを使用できます。
直接参照: ターゲットへのポインタ、相対オフセット、またはターゲットを間接的に見つけることができるハンドル。
初期化フェーズは、クラス コンストラクターの
クラス コンストラクター
コンパイラが収集する順序は、ソース ファイル内でステートメントが出現する順序によって決まります。静的コード ブロックは、静的ブロックの前に定義された変数、静的ブロックの後に定義された変数、および静的ブロックで定義された変数にのみアクセスできます。前の静的ブロックに値を割り当てることはできますが、アクセスすることはできません。
非法向前引用示例 public class SuperClass { public static int va; static { value = 1; //可以编译通过 va = value; //报错 非法向前引用 System.out.println("父类初始化"); } public static int value = 123; }
静的ブロックはインターフェイスでは使用できませんが、変数割り当て操作は実行できるため、インターフェイスとクラスは
仮想マシンは、クラスの
仮想マシン設計チームは、「クラスの完全修飾名を通じてこのクラスを記述するバイナリ バイト ストリームを取得する」というアクションをクラス ローディングに Java に実装しました。これにより、アプリケーションは必要なクラスを取得する方法を決定できるようになります。このアクションを実装するコードのブロックは、クラス ローダーと呼ばれます。
ブートストラップ クラスローダー (ブートストラップ クラスローダー):
拡張クラスローダー: このローダーは、
アプリケーション クラスローダー: このローダーは sun.misc.Launcher$AppClassLoader によって実装され、ユーザー クラス パス (ClassPath) で指定されたクラス ライブラリをロードします。開発者はこのローダーを直接使用できます。アプリケーションにカスタム クラス ローダーがない場合、これがプログラムによってデフォルトで実行されるクラス ローダーになります。 (システムローダー)
我们的应用程序都是由这3种类加载器相互配合进行加载的。如果有必要,还可以加入自定义的类加载器。
这些类加载器之间的关系如下图:
双亲委派模型的工作过程是:如果一个类加载器收到了一个类加载请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一层的加载器都是如此,因此所有的加载请求最终都应该到达顶层的启动类加载器。只有当父加载无法完成这个加载请求时,子加载器才会尝试自己去加载。
1、当ApplicationClassLoader加载一个class时,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器ExtClassLoader去完成。
2、当ExtClassLoader加载一个class时,它首先也不会自己去尝试加载这个类,而是把类加载请求委派给BootStrapClassLoader去完成。
3、如果BootStrapClassLoader加载失败(例如在$JAVA_HOME/jre/lib里未查找到该class),会使用ExtClassLoader来尝试加载;
4、若ExtClassLoader也加载失败,则会使用AppClassLoader来加载,如果AppClassLoader也加载失败,则会报出异常ClassNotFoundException。
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // 先检查此类是否已被加载 Class<?> c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { //委派给父类加载器去加载 if (parent != null) { c = parent.loadClass(name, false); } else { //如果没有父加载器,则调用启动类加载器 c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } //如果父加载器无法加载,则调用本身加载器去加载 if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; } }
系统类防止内存中出现多份同样的字节码
保证Java程序安全稳定运行
参考
《深入理解Java虚拟机》
总结:以上就是本篇文的全部内容,希望能对大家的学习有所帮助。更多相关教程请访问Java视频教程,java开发图文教程,bootstrap视频教程!
以上がJava学習のためのJvmクラスロードメカニズムの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。