JVMクラスローディングサブシステムの解毒について話しましょう

JVMクラスローディングサブシステムの解毒について話しましょう

[[325862]]

ライブインタビュー

  • あなたの履歴書を見ると、JVM に精通しているようですが、クラスのロード プロセスについて教えていただけますか?
  • 使用する String クラスをカスタマイズできますか?
  • クラスローダーとは何ですか?
  • マルチスレッドの場合、クラスのロードが繰り返し行われないのはなぜですか?
  • 親委任メカニズムとは何ですか?その利点は何ですか?このメカニズムは破壊できるのでしょうか?

[[325863]]

クラスローディングサブシステム

クラスローディングメカニズムの概念

Java 仮想マシンは、クラスを記述するデータをクラス ファイルからメモリに読み込み、そのデータを検証、変換、解析、初期化して、最終的に仮想マシンが直接使用できる Java 型を形成します。これは仮想マシンの読み込みメカニズムです。クラス ファイルがクラス ローダーによってロードされた後、クラス構造を記述するメタデータ オブジェクトが JVM 内に形成されます。このメタデータ オブジェクトを通じて、コンストラクター、プロパティ、メソッドなどのクラスの構造情報を取得できます。 Java では、ユーザーはこのクラス関連のメタデータ オブジェクトを通じてクラス オブジェクトの関数を間接的に呼び出すことができます。よく見かけるClassクラスです。

クラスローディングサブシステムの役割

  • クラス ローディング サブシステムは、ファイル システムまたはネットワークからクラス ファイルをロードする役割を担います。クラス ファイルには、ファイルの先頭に特定のファイル識別子 (0xCAFEBABE) があります。
  • ClassLoader はクラス ファイルの読み込みのみを担当します。実行できるかどうかは実行エンジンによって決定されます。
  • ロードされたクラス情報はメソッド領域と呼ばれるメモリ空間に保存されます。メソッド領域には、クラス情報に加えて、実行時定数プール情報も保存されます。これには、文字列リテラルや数値定数も含まれる場合があります (この定数情報は、クラスファイル内の定数プール部分のメモリマッピングです)。
  • クラスオブジェクトはヒープに保存されます

クラスローダーの役割

クラス ファイルはローカル ハード ディスク上に存在し、デザイナーが紙に描いたテンプレートとして理解できます。テンプレートが実行されると、JVM にロードされ、ファイルに基づいて n 個の同一インスタンスがインスタンス化されます。

クラスファイルはJVMにロードされ、DNAメタデータテンプレートと呼ばれ、メソッド領域に配置されます。

.calssファイル -> JVM -> 最終的にメタデータテンプレートになるこのプロセスでは、トランスポートツール(クラスローダー)が宅配便の役割を果たす必要があります。

クラスのロードプロセス

クラスのライフ サイクル全体は、仮想マシンのメモリにロードされたときに始まり、メモリからアンロードされたときに終了します。ライフサイクル全体には、読み込み、検証、準備、解析、初期化、使用、アンロードの 7 つの段階が含まれます。 (検証、準備、解析を総称して接続と呼びます。Java 言語のランタイム バインディングをサポートするために、初期化後に解析フェーズを実行することもできます。上記のシーケンスは開始シーケンスにすぎません。実際のプロセスは混在して実行されます。ロード プロセス中に検証が開始されている場合があります。)

1. 読み込み中:

  1. 完全修飾名でクラスを定義するバイナリバイトストリームを取得します。
  2. このバイトストリームによって表される静的ストレージ構造をメソッド領域のランタイムデータ構造に変換します。
  3. メソッド領域内のこのクラスのさまざまなデータへのアクセスポイントとして、このクラスを表す java.lang.Class オブジェクトをメモリ内に生成します。

.calss ファイルを読み込む方法

  • ローカルシステムから直接ロードする
  • ネットワーク経由で取得、典型的なシナリオ: Webアプレット
  • zip圧縮ファイルから読み取り、将来のjarおよびwar形式の基礎となる
  • 実行時計算生成、最も一般的に使用されるのは、動的プロキシ技術です。
  • JSPアプリケーションなどの他のファイルから生成される
  • 比較的まれな、独自のデータベースから.classファイルを抽出する
  • 暗号化されたファイルから取得され、クラスファイルが逆コンパイルされるのを防ぐための一般的な保護手段

2. リンク

確認する

  • 目的は、クラス ファイルのバイト ストリームに含まれる情報が現在の仮想マシンの要件を満たし、ロードされたクラスの正確性を確保し、仮想マシン自体のセキュリティを危険にさらさないことを確認することです。
  • 主にファイル形式の検証、メタデータの検証、バイトコードの検証、シンボル参照の検証の4種類の検証が含まれます。

準備する

  • クラス変数にメモリを割り当て、クラス変数のデフォルトの初期値、つまりゼロ値データ型ゼロ値int0long0Lshort(short)0char'\\u0000'byte(byte)0booleanfalsefloat0.0fdouble0.0dreferencenullを設定します。
  • これには、finalによって変更された静的は含まれません。finalはコンパイル時に割り当てられ、初期化は準備段階で表示されるからです。
  • ここではインスタンス変数は割り当てられず、初期化されません。クラス変数はメソッド領域に割り当てられますが、インスタンス変数はオブジェクト private static int i = 1; とともに Java ヒープ領域に割り当てられます。 // 変数 i には準備段階でのみ値 0 が割り当てられ、初期化中に値 1 が割り当てられます private final static int j = 2; //ここでfinalによって変更された変数jは直接定数となり、コンパイル時に値2が割り当てられます。

解決する

  • 定数プール内のシンボリック参照を直接参照に変換するプロセス
  • 実際、解析操作は JVM が初期化を完了した後に実行されることがよくあります。
  • シンボリック参照は、参照先のオブジェクトを記述するシンボルのセットです。シンボリック参照のリテラル形式は、Java 仮想マシン仕様のクラス ファイル形式で明確に定義されています。直接参照は、ターゲットへの直接のポインター、相対オフセット、または間接的にターゲットを特定するハンドルです。
  • 解析アクションは主にクラスまたはインターフェース、フィールド、クラスメソッド、インターフェースメソッド、メソッド型などを対象としています。定数プール内のCONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_infoなどに対応します。

3. 初期化

  • 初期化フェーズでは、クラスのコンストラクタメソッドを実行します。 ()のプロセス
  • このメソッドを定義する必要はありません。これは javac コンパイラによって自動的に収集され、クラス内のすべてのクラス変数の割り当てアクションと静的コード ブロック内のステートメントとマージされます。
  • コンストラクター メソッド内の命令は、ソース ファイルに表示される順序で実行されます。
  • ()はクラスコンストラクタとは異なります(コンストラクタは仮想マシンの観点からのものです())
  • クラスに親クラスがある場合、JVMはサブクラスが()実行前に、親クラス()が実行されました
  • 仮想マシンはクラスが() メソッドはマルチスレッドで同期的にロックされます
  1. パブリッククラスClassInitTest{
  2. プライベートスタティック 整数1 = 30;
  3. 静的{
  4. 数値1 = 10;
  5. 数値2 = 10; //num2 は変数定義の前に記述されていますが、なぜエラーが発生しないのでしょうか? ?
  6. システム。出力.println(num2); //ここで直接印刷できますか?エラー、不正な前方参照、割り当てることはできますが、呼び出すことはできません
  7. }
  8. プライベートスタティック 整数2 = 20; //num2は準備段階でデフォルトの初期値0に設定され、初期化段階で10から20に変更されます。
  9. 公共 静的void main(String[] args){
  10. システム。出力.println(num1); //10
  11. システム。出力.println(num2); //20
  12. }
  13. }

クラスの積極的および受動的な使用

Java プログラムでは、クラスを能動的な使用と受動的な使用の 2 つの方法で使用します。仮想マシン仕様では、クラスを直ちに「初期化」する必要がある、つまりクラスをアクティブに使用する必要がある状況は 5 つだけであると規定されています。

  • クラスのインスタンスを作成する、クラスまたはインターフェースの静的変数にアクセスする、静的変数に値を割り当てる、またはクラスの静的メソッドを呼び出す(つまり、4 つのバイトコード命令 new、getstatic、putstatic、invokestatic に遭遇したとき)
  • 反射
  • クラスのサブクラスの初期化
  • Java仮想マシンの起動時にスタートアップクラスとしてマークされるクラス
  • JDK7 によって提供される動的言語サポート: java.lang.invoke.MethodHandle インスタンスの解析結果。 REF_getStatic、REF_putStatic、REF_invokeStaticハンドルに対応するクラスが初期化されていない場合は、初期化します。

上記の 5 つのケースを除き、Java クラスを使用するその他の方法は、クラスの受動的な使用と見なされ、クラスの初期化にはつながりません。

例えば:

  1. パブリッククラスNotInitialization {
  2. 公共 静的void main(String[] args) {
  3. // SupperClass int 123 のみを出力し、SubClass init は出力しません
  4. //静的フィールドの場合、このフィールドを直接定義するクラスのみが初期化されます
  5. システム。出力.println(SubClass.value);
  6. }
  7. }
  8. クラススーパークラス {
  9. 静的{
  10. システム。 .println( "SupperClass init" );を出力します
  11. }
  12. 公共 静的 整数値 = 123;
  13. }
  14. クラス SubClass は SuperClass を拡張します {
  15. 静的{
  16. システム。 .println ( "サブクラスの初期化" );
  17. }
  18. }

クラスローダー

  • JVM は、Bootstrap ClassLoader と User-Defined ClassLoader という 2 種類のクラス ローダーをサポートしています。
  • 概念的には、カスタム クラス ローダーとは、通常、プログラム内で開発者によってカスタマイズされたクラス ローダーを指します。ただし、Java 仮想マシン仕様では、このように定義されていません。代わりに、抽象クラス ClassLoader から派生したすべてのクラス ローダーは、カスタム クラス ローダーとして分類されます。

ブートストラップクラスローダー

  • このクラス ローダーは C/C++ で実装され、JVM 内にネストされています。
  • これは、Java のコア ライブラリ (JAVA_HOME/jre/lib/rt.jar、resource.jar、または sun.boot.class.path パスの下のコンテンツ) をロードして、JVM 自体に必要なクラスを提供するために使用されます。
  • java.lang.ClassLoader を継承せず、親ローダーもありません
  • 拡張クラスとアプリケーションクラスローダーをロードし、それらを親クラスローダーとして指定します。
  • セキュリティ上の理由から、Boostrap ブート クラス ローダーは、java、Javax、sun などで始まるクラスのみをロードします。

拡張クラスローダー

  • Java で記述され、sun.misc.Launcher$ExtClassLoader によって実装されています
  • ClassLoaderから派生
  • 親クラスローダーはブートクラスローダーです
  • java.ext.dirs システム プロパティで指定されたディレクトリ、または JDK インストール ディレクトリの jre/lib/ext サブディレクトリ (拡張ディレクトリ) からクラス ライブラリをロードします。ユーザーが作成したJARがこのディレクトリに置かれると、拡張クラスローダーによって自動的にロードされます。

アプリケーション クラス ローダー (システム クラス ローダー、AppClassLoader とも呼ばれます)

  • Java で記述され、sun.misc.Lanucher$AppClassLoader によって実装されています
  • ClassLoaderから派生
  • 親クラスローダーは拡張クラスローダーである
  • 環境変数classpathまたはシステムプロパティjava.class.pathで指定されたパスのクラスライブラリをロードする役割を担います。
  • このクラス ローダーは、プログラム内のデフォルトのクラス ローダーです。一般的に、Java アプリケーション クラスはこれによってロードされます。
  • クラスローダーはClassLoader#getSystemClassLoader()メソッドを通じて取得できます。
  1. パブリッククラスClassLoaderTest {
  2. 公共 静的void main(String[] args) {
  3. //システムクラスローダーを取得する
  4. クラスローダー systemClassLoader = ClassLoader.getSystemClassLoader();
  5. システム。出力.println(systemClassLoader); //sun.misc.Launcher$AppClassLoader@135fbaa4
  6. //上位層を取得する: 拡張クラスローダー
  7. クラスローダー extClassLoader = systemClassLoader.getParent();
  8. システム。 .println(extClassLoader)を出力します//sun.misc.Launcher$ExtClassLoader@2503dbd3
  9. //上位層を再度取得: ブートクラスローダーを取得できません
  10. クラスローダー bootstrapClassLoader = extClassLoader.getParent();
  11. システム。 .println(bootstrapClassLoader)を出力します//ヌル 
  12. // ユーザー定義クラスの場合、デフォルトでシステムクラスローダーがロードに使用され、出力はsystemClassLoaderと同じです。
  13. クラスローダー classLoader = ClassLoaderTest.class.getClassLoader();
  14. システム。出力.println(classLoader); //sun.misc.Launcher$AppClassLoader@135fbaa4
  15. //String クラスはブートストラップ クラス ローダーを使用してロードされます。 Java のコア クラス ライブラリはブートストラップ クラス ローダーを使用してロードされるため、取得できません。
  16. クラスローダー classLoader1 = String.class.getClassLoader();
  17. システム。出力.println(classLoader1); //ヌル 
  18. //BootstrapClassLoader がロードできる API のパスを取得します
  19. URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
  20. (URL url: urls)の場合{
  21. システム。出力.println(url.toExternalForm());
  22. }
  23. }
  24. }

ユーザー定義クラスローダー

日常的な Java アプリケーション開発では、クラスのロードはほとんどの場合、3 つのクラス ローダーが連携して実行されます。必要に応じて、クラスローダーをカスタマイズして、クラスのロード方法をカスタマイズすることもできます。

クラスローダーをカスタマイズする理由は何ですか?

  • ロードクラスを分離する
  • クラスのロード方法を変更する
  • 拡張された読み込みソース (データベース、クラウドなどの指定されたソースからクラスを読み込むことができます)
  • ソースコードの漏洩を防ぐ (Java コードは簡単に逆コンパイルされます。暗号化されている場合、カスタム ローダーは最初にクラスを復号化してロードできます)

ユーザー定義ローダーの実装手順

  1. 開発者は、抽象クラスjava.lang.ClassLoaderを継承することで、特別なニーズを満たす独自のクラスローダーを実装できます。
  2. JDK1.2 より前では、クラス ローダーをカスタマイズする場合は、常に ClassLoader クラスを継承し、loadClass() メソッドをオーバーライドしてカスタム クラス ローディングを実装していました。ただし、JDK1.2 以降では、loadClass() メソッドをオーバーライドすることは推奨されなくなりました。代わりに、findClass() メソッドにカスタム クラスの読み込みロジックを記述することをお勧めします。
  3. カスタム クラス ローダーを作成するときに、過度に複雑な要件がない場合は、URLClassLoader クラスを直接継承することができます。これにより、findClass() メソッドとバイトコード ストリームを取得する方法を自分で作成する必要がなくなり、カスタム クラス ローダーの作成がより簡潔になります。

ClassLoader 共通メソッド

ClassLoader クラスは抽象クラスであり、後続のすべてのクラスローダーは ClassLoader から継承されます (スタートアップクラスローダーを除く)

メソッド 説明 getParent() このクラスローダーのスーパークラスローダーを返します loadClass(String name) name という名前のクラスをロードし、java.lang.Class クラスのインスタンスを返します findClass(String name) name という名前のクラスを検索し、java.lang.Class クラスのインスタンスを返します findLoadedClass(String name) ロードされている name という名前のクラスを検索し、java.lang.Class クラスのインスタンスを返します defineClass(String name, byte[] b, int off, int len) バイト配列 b の内容を Java クラスに変換し、java.lang.Class クラスのインスタンスを返します resolveClass(Class c) 指定されたJavaクラスに接続する

クラスローダーへの参照

JVM は、型がブートストラップ ローダーによってロードされたか、ユーザー クラス ローダーによってロードされたかを認識する必要があります。型がユーザー クラス ローダーによってロードされる場合、JVM は、このクラス ローダーへの参照を型情報の一部としてメソッド領域に保存します。ある型から別の型への参照を解決する場合、JVM は両方の型のクラス ローダーが同じであることを確認する必要があります。

親の委任メカニズム

Java 仮想マシンは、クラス ファイルに対してオンデマンド ロード方式を使用します。つまり、クラスが必要になると、そのクラス ファイルがメモリにロードされ、クラス オブジェクトが生成されます。さらに、特定のクラスのクラス ファイルをロードする場合、Java 仮想マシンは親委任モードを採用します。つまり、要求は処理のために親クラスに引き渡されます。タスク委任モードです。

作業工程

  • クラス ローダーがクラスのロード要求を受信した場合、最初にクラス自体をロードするのではなく、実行のために親クラス ローダーに要求を委任します。
  • 親クラス ローダーにまだ親クラス ローダーがある場合は、さらに上方に再帰的に委任され、要求は最終的に最上位のスタートアップ クラス ローダーに到達します。
  • 親クラスローダーがクラスロードタスクを完了できる場合、正常に戻ります。親クラスローダーがロードタスクを完了できない場合、子クラスローダーは独自にロードを試みます。これは親委任モードです。

利点

  • クラスの重複読み込みを避けてください。 JVM では、クラス名だけでなく、さまざまなクラスが区別されます。異なる ClassLoader によってロードされた同じクラス ファイルは、2 つの異なるクラスに属します (たとえば、Java の Object クラスの場合、どのクラス ローダーがこのクラスをロードしようとしても、最終的にはモデルの最上位にあるスタートアップ クラス ローダーにロードが委任されます。親委任モデルが採用されておらず、各クラス ローダーが独自にロードする場合、システムには多くの異なる Object クラスが存在することになります)
  • プログラムのセキュリティを保護し、コア API が勝手に改ざんされるのを防ぎ、カスタム クラス java.lang.String などのコア Java クラスがユーザー作成クラスによって動的に置き換えられるのを防ぎます。

JVM では、2 つのクラス オブジェクトが同じクラスであるかどうかを示すために必要な条件が 2 つあります。

  • クラスの完全なクラス名はパッケージ名を含めて一貫している必要があります。
  • このクラスをロードするClassLoader(ClassLoaderインスタンスオブジェクトを参照)は同じである必要があります

サンドボックスセキュリティメカニズム

String クラスをカスタマイズする場合、最初にブートストラップ クラス ローダーを使用してカスタム String クラスがロードされます。ロード プロセス中に、ブートストラップ クラス ローダーは最初に JDK に付属するファイル (rt.jar パッケージ内の java\lang\String.class) をロードします。エラー メッセージには、rt.jar パッケージ内の String クラスがロードされているため、main メソッドが存在しないことが示されています。これにより、シンプルなサンドボックス セキュリティ メカニズムである Java コア ソース コードの保護が保証されます。

親の委任モデルの破壊

  • 親委任モデルは必須の制約モデルではなく、Java 設計者が開発者に推奨するクラスローダー実装方法です。クラス ローダーをカスタマイズし、loadClass() メソッドをオーバーライドし、新しい読み込みロジックを指定する限り、これを「破棄」できます。 findClass() メソッドをオーバーライドしても、親の委任は破棄されません。
  • 親委任モデルに問題があります。最上位レベルの ClassLoader は最下位レベルの ClassLoader のクラスをロードできません。代表的な例としては、JNDI や JDBC があり、スレッド コンテキスト クラス ローダー (Thread Context ClassLoader) が追加されます。クラス ローダーは Thread.setContextClassLoaser() を通じて設定でき、その後、最上位の ClassLoader は Thread.getContextClassLoader() を使用して、ロードするための基礎となる ClassLoader を取得します。
  • Tomcat はカスタム ClassLoader を使用し、親の委任メカニズムも破棄します。各アプリケーションは、WebAppClassloader を使用して個別にロードされます。まず、WebAppClassloader を使用してクラスをロードします。ロードできない場合は、親ローダーにロードを委任します。これにより、各アプリケーション内のクラスが競合しないことが保証されます。各 Tomcat に複数のプロジェクトをデプロイできます。各プロジェクトには、多数の同一のクラス ファイル (多数の同一の jar パッケージ) が存在します。これらは互いに干渉することなく JVM にロードできます。
  • 親委任の破棄を使用してホット コード置換を実装します (クラス ファイルが変更されるたびにサービスを再起動する必要はありません)。クラスは ClassLoader によって 1 回しかロードできないため、それ以外の場合は java.lang.LinkageError が報告されます。ホットコードデプロイメントを実装する場合は、新しいクラスファイルをロードするたびにカスタム ClassLoader を作成できます。この機能を使用することで、JSP の動的な変更が可能になります。

<<:  RedisやZooKeeperなどの分散ロックの原理に関する考察

>>:  IBM社長ジム・ホワイトハースト:ハイブリッドクラウドで大規模なイノベーションを加速

推薦する

今こそクラウドコンピューティングのデータの霧を晴らす時です

今日の企業は、クラウド アプリケーションやモバイル デバイスなど、多くのテクノロジを導入しており、従...

テクノロジーの選択: Docker コンテナ エンジン

私は最近 Docker と Kubernetes を勉強し、数日前に技術共有セッションを行いました。...

今後のクラウドコンピューティングの注目のトレンドは何でしょうか?

コスト効率に加えて、企業におけるクラウド コンピューティング アプリケーションのその他の利点としては...

cloudcone: 旧正月ギフト、年間 13.5 ドル、1G メモリ/2 コア/40G ハード ドライブ/5T トラフィック

Cloudcone は、皆様への恩返しとして、旧正月に小さなイベントを開催しました。概要は次のとおり...

ウェブサイトの最適化における新規訪問者と既存訪問者の関係についての簡単な説明

今ではほとんどのウェブサイトが検索エンジンに依存していることは誰もが知っています。私たちは継続的に最...

テンセントクラウドがチャレンジャーズクアドラントに選出され、2023年ガートナーコンテナ管理マジッククアドラントが発表

記者は10月17日、テンセントクラウドがガートナーが発表したばかりの2023年「コンテナ管理のマジッ...

乾物シェアリング:短期間で百度の重みを高めるための必要条件

すべてのウェブマスターは、ウェブサイトの Baidu の重みをもっと気にするようになると思います。B...

シャミの命はアリババによって遅らされた

著者:蘇奇 編集者:金玉凡出典: 神蘭財経あなたがXiami Music の真のファンなら、自分の ...

エッジ サービスが企業のレジリエンス戦略に不可欠な 5 つの理由

コンピューティングに関して言えば、エッジ コンピューティングが現在の流行語です。これはなぜでしょうか...

おすすめ: a2hosting - 無制限の SSD ホスティング/cpanel が 35% オフ

11 月 28 日から 12 月 2 日の間に、老舗ホスティング ブランドである a2hosting...

Helm とは何ですか?クラウドネイティブ アプリケーションのプライベート展開の効率はどのように向上しますか?

みなさんこんにちは。私はブラザー・スネイルです。 50 個のマイクロサービスを持つプロジェクトがあり...

Python バッチマイニング Baidu ドロップダウン ボックス キーワード

Baidu のドロップダウン ボックスのキーワードは、SEO キーワード拡張のための強力なツールとし...

JD.comの「システム紛争」:技術を理解する劉強東が技術をボトルネックに

年末、JD.comは再び外部から幅広い注目を集めました。今年初めにオラクルを退社したCTOの王亜青氏...

組織はクラウドに潜んでいる人物を把握する必要がある

ほとんどの組織は、ユーザーがクラウド コンピューティング環境で何ができるかを完全に理解していません。...

WeChat パブリックアカウントのプロモーション: ホットスポットを活用して新規ユーザーを引き付けるには?

自己紹介をさせてください。私はニューメディア業界で3年間働いてきました。最初の1年間はニューメディア...