JVM パフォーマンスの最適化 - クラスローダー、クラスの手動ホットロード

1. クラスロード機構の階層

作成された「.java」拡張子を持つ各クラス ファイルには、実行する必要があるプログラム ロジックが格納されます。これらの「.java」ファイルは、Java コンパイラによって拡張子「.class」を持つファイルにコンパイルされます。「.class」ファイルは、 Java コードが変換された後の仮想マシンの命令に従って、特定のクラスを使用する必要がある場合、仮想マシンはその「.class」ファイルをロードし、対応するクラス オブジェクトを作成し、そのクラス ファイルをメモリにロードします。このプロセスはクラス ロードと呼ばれます。ここでは、次のようにクラス ロードのプロセスを理解する必要があります。

Jvmはクラスファイルを実行します

ステップ 1. クラスロードメカニズム

クラス ファイルのバイトコード コンテンツをメモリにロードし、これらの静的データをメソッド領域のランタイム データ構造に変換し、このクラスを表す java.lang.Class オブジェクトをメソッド領域のクラス データとしてヒープ内に生成します。入口にアクセスするには、このプロセスにはクラスローダーの参加が必要です。

システムの実行中に、クラス ローダーは .class ファイルのバイナリ データを外部メモリ (CD、ハードディスクなど) からメモリに転送し、CPU はメモリから命令とデータを読み取って演算を実行し、保存します。演算結果はメモリに保存されます。この処理ではメモリが「セッター」の役割を果たしますが、平たく言えば、メモリがない場合、クラスローダは.classファイルのバイナリデータを外部記憶装置から直接CPUに転送して処理しますが、処理速度が速いため、 CPU がはるかに高速です。 データ転送速度が速いため、データの切断が発生しやすいため、バッファリングするためのメモリが必要です。

クラスが実行時に .class ファイルをメソッド領域にロードした後、Java.lang.Class オブジェクトがヒープ内に作成され、メソッド領域内のクラスのデータ構造がカプセル化されます。Class オブジェクトは、次のプロセス中に作成されます。クラスをロードします。はい、各クラスは Class タイプのオブジェクトに対応します。Class クラスのコンストラクターはプライベートであり、JVM によってのみ作成できます。したがって、Class オブジェクトはリフレクションへの入り口であり、このオブジェクトを使用すると、ターゲット クラスに関連付けられた .class ファイル内の特定のデータ構造を取得できます。


クラス ロードの最終生成物は、ヒープ内にある Class オブジェクト (ターゲット クラス オブジェクトではないことに注意してください) です。このオブジェクトは、クラスのデータ構造をメソッド領域にカプセル化し、データにアクセスするためのインターフェイスをユーザーに提供します。 Java リフレクションインターフェイスであるメソッド領域の構造。

现在我也找了很多测试的朋友,做了一个分享技术的交流群,共享了很多我们收集的技术文档和视频教程。
如果你不想再体验自学时找不到资源,没人解答问题,坚持几天便放弃的感受
可以加入我们一起交流。而且还有很多在自动化,性能,安全,测试开发等等方面有一定建树的技术大牛
分享他们的经验,还会分享很多直播讲座和技术沙龙
可以免费学习!划重点!开源的!!!
qq群号:110685036

ステップ2. 接続プロセス

Java クラスのバイナリ コードを JVM の実行状態にマージするプロセス

検証: ロードされたクラス情報が JVM 仕様に準拠しており、セキュリティ上の問題がないことを確認します。

準備:メソッド領域にクラス変数(静的変数)のメモリを正式に確保し、クラス変数の初期値を設定する段階。

分析: 仮想マシンの定数プールのシンボリック参照がバイト参照プロセスに置き換えられる

ステップ 3. 初期化

初期化フェーズは、クラス コンストラクター メソッドを実行するプロセスです<clinit>()クラス コンストラクター<clinit>()メソッドは、コンパイラーがクラス内のすべてのクラス変数の代入アクションを自動的に収集し、静的ステートメント ブロック (静的ブロック) 内のステートメントをマージすることによって生成され、コードは上から下に実行されます。

クラスを初期化するときに、その親クラスが初期化されていないことが判明した場合は、最初に親クラスの初期化をトリガーする必要があります。

仮想マシンは、クラスの<clinit>()メソッドがマルチスレッド環境で正しくロックされ、同期されていることを保証します。Java
クラスの静的フィールドにスコープ設定されている場合、このフィールドを実際に宣言するクラスのみが初期化されます。

2. クラスローダーの階層

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

拡張クラスローダー

システム (-) クラスローダー

1. (ブートストラップ) クラスローダーを開始します。

<JAVA_HOME>/libスタートアップ クラス ローダーは主に JVM 自体に必要なクラスをロードします。このクラス ロードは C++ 言語で実装され、仮想マシン自体の一部です。パスの下のコア クラス ライブラリまたはパスの下の jar パッケージをロードします。-Xbootclasspath パラメータで指定されています。メモリにロードします。仮想マシンは、rt.jar などのファイル名に従って jar パッケージを認識し、ロードすることに注意してください。ファイル名が仮想マシンによって認識されない場合は、たとえjar パッケージは lib ディレクトリにスローされますが、効果はありません (セキュリティ上の理由により、ブートストラップ起動クラス ローダーは、パッケージ名が java、javax、sun などで始まるクラスのみをロードします)。

2. 拡張クラスローダー

拡張クラスローダーは、Sun (Oracle が買収) によって実装された sun.misc.Launcher$ExtClassLoader クラスを参照します。これは Java 言語で実装され、Launcher の静的な内部クラスです。ディレクトリのロードまたはシステム変数-Djava <JAVA_HOME>/lib/ext. ext.dir は、ロケーション パス内のクラス ライブラリを指定し、開発者は標準の拡張クラス ローダーを直接使用できます。

3. システムクラスローダー

アプリケーション ローダーとも呼ばれ、Sun によって実装された sun.misc.Launcher$AppClassLoader を指します。これは、システム クラス パス java -classpath または -D java.class.path で指定されたパスの下にクラス ライブラリをロードする役割を果たします。これは、私たちがよく使用するクラスパス パスです。開発者は、システム クラス ローダーを直接使用できます。通常の状況では、 、このクラスがロードされます。これはプログラム内のデフォルトのクラスローダーであり、ClassLoader#getSystemClassLoader() メソッドを通じて取得できます。

Java の日常的なアプリケーション開発では、クラスのロードはほとんど上記 3 つのクラスローダーが連携して実行されますが、必要に応じてクラスローダーをカスタマイズすることもできます。オンデマンド ロード方式。つまり、クラスを使用する必要があるときに、そのクラス ファイルがメモリにロードされてクラス オブジェクトが生成されます。特定のクラスのクラス ファイルをロードするときに、Java 仮想マシンは親委任モード。つまり、リクエストは処理のために親クラスに渡されます。これはタスク委任モデルです。以下で詳細を学びましょう。

3.1. 親の委任モデルを理解する

以下では、Java で定義されたいくつかのクラス ローダーと、コード レベルからの親委任モードの実装について学びます。それらのクラス図の関係は次のとおりです。

親委任モードは Java 1.2 以降に導入されました。動作原理は、クラス ローダーがクラス ロード リクエストを受信した場合、最初にそれをロードせず、リクエストを親クラスのローダーに委任することです。親クラスの場合の実行ローダーにはまだ親クラス ローダーがあり、それはさらに上位に再帰的に委任され、リクエストは最終的に最上位の起動クラス ローダーに到達します。親クラス ローダーがクラス ロード タスクを完了できれば、正常に戻ります。親クラス ローダーはこのロード タスクを完了できません。子ローダーはそれを自分でロードしようとします。これは親委任モデルです。つまり、各息子は怠惰で、父親が仕事をするまでは、仕事があるたびに父親に任せます。息子は私ができないことを自分で見つけてやり遂げる、これが伝説の強さの秘訣ではないでしょうか。では、このモデルを採用することに何の意味があるのでしょうか?

3.1. 保護者委任モデルの利点

親委任モードを使用する利点は、Java クラスがそのクラス ローダーと優先順位の階層関係を持っていることです。この階層関係により、父親がすでにクラスをロードしている場合にクラスの繰り返しロードを回避できます。サブクラスローダーを再度ロードします。次に、セキュリティ要素を考慮して、Java コア API で定義された型が任意に置き換えられることはなく、java.lang.Integer という名前のクラスがネットワーク経由で親委任モードを通じて起動クラス ローダーに渡され、スタートアップ クラス ローダー この名前のクラスがコア Java API で見つかり、そのクラスがロードされていることがわかります。ネットワーク経由で渡された java.lang.Integer は再ロードされませんが、ロードされた Integer.class は直接ロードされます。これにより、コア API がロードされなくなる可能性があります。ライブラリは任意に改ざんされています。クラスパス上の java.lang.SingleInterge という名前のクラス (このクラスは作成されています) をカスタマイズしたらどうなるのかと考えているかもしれません。このクラスは java.lang に存在しません。親委任モードを通じて起動クラス ローダーに渡されます。このクラスは親クラス ローダーのパスに存在しないため、ロードされず、逆に委任されます。ロード用の子クラス ローダー。クラスは最終的にシステム クラス ローダーを通じてロードされます。ただし、java.lang はコア API パッケージであり、アクセス許可が必要であるため、これは許可されません。強制的にロードすると、次の例外が報告されます。

java.lang.SecurityException: 禁止されているパッケージ名: java.lang

したがって、どうやっても正常にロードできません。

3. クラスローダ間の関係

クラスローダー間の関係(継承関係には言及しません)をさらに理解すると、次の 4 つの点に分けることができます。

  • 親クラスを使用せずに、C++ で実装されたクラス ローダーを起動します。
  • 拡張クラスローダー (ExtClassLoader)、Java 言語で実装、親クラスローダーは null
  • システム クラス ローダー (AppClassLoader)。Java 言語で実装され、親クラス ローダーは ExtClassLoader です。
  • カスタム クラス ローダー。親クラス ローダーは AppClassLoader である必要があります。

1. クラスローダーの共通メソッド

ロードクラス(文字列)

このメソッドは、指定された名前 (パッケージ名を含む) を持つバイナリ型をロードします。このメソッドは、JDK1.2 以降、ユーザーによる書き換えは推奨されなくなりましたが、ユーザーはこのメソッドを直接呼び出すことができます。loadClass() メソッドは、ClassLoader クラス自体によって実装されます。 . このメソッドでは、ロジックは親委任モードの実装です。ソース コードは次のとおりです。loadClass(String name, booleansolve) はオーバーロードされたメソッドです。resolve パラメーターは、クラス オブジェクトを生成し、解析関連の操作を実行するかどうかを表します。同時に。

loadClassメソッドにあるように、クラスのロード要求が来ると、まずクラスオブジェクトをキャッシュ内で検索し、存在する場合は直接返し、存在しない場合は親ローダに渡してロードします。クラスをロードします。ロードする親がない場合は、トップレベルのスタートアップ クラス ローダーにロードさせます。それでも見つからない場合は、findClass() メソッドを使用してロードします (findClass() は後でさらに紹介します) )。また、loadClass の実装から、クラスをロードするためのルールを再定義したくない場合、複雑なロジックがなく、実行時に指定したクラスをロードしたいだけであれば、これを直接使用できることもわかります。 .getClass().getClassLoder.loadClass("className" ) を使用すると、ClassLoader のloadClass メソッドを直接呼び出してクラス オブジェクトを取得できます。

findClass(文字列)

JDK1.2 より前では、カスタム クラスをロードするときは、常に ClassLoader クラスを継承し、loadClass メソッドをオーバーライドしてカスタム クラス ロード クラスを実装していましたが、JDK1.2 以降は、loadClass() をオーバーライドすることは推奨されなくなりました。 , ただし、カスタム クラス ロード ロジックを findClass() メソッドに記述することをお勧めします。前の分析から、findClass() メソッドがloadClass() メソッド内で呼び出されていることがわかります。 () メソッド ロードが失敗した後、独自の findClass() メソッドが呼び出されてクラスのロードを完了し、カスタマイズされたクラス ローダーが親委任モードにも準拠していることを確認します。findClass() メソッドの特定のコード ロジックは ClassLoader クラスには実装されていないことに注意してください。代わりに、ClassNotFoundException 例外がスローされます。同時に、findClass メソッドは通常、 defineClass メソッド (後で分析します)

defineClass(byte[] b, int off, int len)

defineClass() メソッドは、バイト ストリームを解析して、JVM によって認識できる Class オブジェクトにするために使用されます (このメソッドのロジックは ClassLoader に実装されています)。このメソッドを通じて、クラス オブジェクトは、クラスファイルだけでなく、ネットワーク経由でクラスのバイトコードを受信し、それをバイトストリームに変換して対応する Class オブジェクトを作成するなど、他のメソッドを介したクラスオブジェクトも使用できます。defineClass() メソッドは通常、 findClass() メソッド 通常、カスタム クラス ローダーでは、クラスがロードされるときに、ClassLoader の findClass() メソッドを直接オーバーライドし、ロード ルールを記述します。ロードされるクラスのバイトコードを取得した後、変換されます。ストリームに追加すると、defineClass() メソッドが呼び出されて、クラスの Class オブジェクトが生成されます。

solveClass(クラス<?> c)

このメソッドを使用すると、クラスの Class オブジェクトの作成と解析を同時に行うことができます。先ほど、リンク ステージでは主にバイトコードを検証し、クラス変数にメモリを割り当て、バイトコード ファイル内のシンボル参照を直接参照に変換しながら初期値を設定すると述べました。

4. ホットデプロイメント

Java アプリケーションの場合、ホット デプロイメントとは、実行時に Java クラス ファイルを更新することです。

1. ホットデプロイの原理は何ですか?

ホット デプロイメントの原理を知りたい場合は、Java クラスのロード プロセスを理解する必要があります。Javaクラスファイルを仮想マシン上のオブジェクトに転送するには、以下の処理が必要です。

まず、Java ファイルは Java コンパイラによってクラス バイトコードにコンパイルされ、クラス ローダーによってクラス バイトコードが読み取られ、クラスがインスタンスに変換されます。NewInstance によってインスタンスのオブジェクトが生成されます。

クラスローダーの ClassLoader 機能は、クラスのバイトコードをクラスのインスタンスに変換することです。

Java アプリケーションでは、すべてのインスタンスがクラス ローダーによってロードされます。

通常、システムでは、クラスのロードはシステム独自のクラス ローダーによって完了し、同じ完全修飾名 (com.csiar.soc.HelloWorld など) を持つ Java クラスは 1 回だけロードでき、ロードすることはできません。 。

この時点で、Java クラスをアンインストールして、新しいバージョンの Java クラスに置き換えたい場合はどうすればよいかという疑問が生じます。

クラスローダーでは、Java クラスは 1 回しかロードできず、アンロードすることはできません。クラスローダーを直接変更することはできますか? 答えは「はい」です。クラス ローダーをカスタマイズして、ClassLoader の findClass メソッドをオーバーライドできます。ホット デプロイメントを実現するには、次の 3 つのステップに分割できます。

  1. カスタム ClassLoader を破棄する
  2. クラスファイルを更新する
  3. 新しい ClassLoader を作成して、更新されたクラス ファイルをロードします。

2. ホットデプロイメントとホットローディング

2.1. Java ホットデプロイメントと Java ホットロードの関係と違い

Java ホット デプロイメントとホット ロードの間の関係

  1. サーバーを再起動せずにプロジェクトをコンパイル/デプロイする
  2. Javaベースのクラスローダーの実装

Java ホットデプロイメントとホットロードの違い

  1. 導入方法
  • ホット デプロイメントでは、サーバーの実行中にプロジェクトが再デプロイされます。
  • ホットリロードは実行時にクラスをリロードします
  • 実施原則
  • ホットデプロイメントはアプリケーション全体を直接リロードします
  • ホットリロードは実行時にクラスをリロードします
  • 使用するシーン
  • ホット デプロイメントは実稼働環境でより一般的に使用されます
  • ホット リロードは、開発環境での使用に適しています。

3. 関連コード

ユーザークラスは変更されていません

public class User {

	public void add() {
		System.out.println("addV1,没有修改过...");
	}
}

ユーザー更新クラス

public class User {

	public void add() {
		System.out.println("我把之前的user add方法修改啦!");
	}
}

カスタムクラスローダー

public class MyClassLoader extends ClassLoader {

	@Override
	protected Class<?> findClass(String name) throws ClassNotFoundException {
		try {
			// 文件名称
			String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
			// 获取文件输入流
			InputStream is = this.getClass().getResourceAsStream(fileName);
			// 读取字节
			byte[] b = new byte[is.available()];
			is.read(b);
			// 将byte字节流解析成jvm能够识别的Class对象
			return defineClass(name, b, 0, b.length);
		} catch (Exception e) {
			throw new ClassNotFoundException();
		}

	}

}

コードを更新する

public class Hotswap {

	public static void main(String[] args)
			throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException,
			SecurityException, IllegalArgumentException, InvocationTargetException, InterruptedException {
		loadUser();
		System.gc();
		Thread.sleep(1000);// 等待资源回收
		// 需要被热部署的class文件
		File file1 = new File("F:\\test\\User.class");
		// 之前编译好的class文件
		File file2 = new File(
				"F:\\test\\test\\target\\classes\\com\\itmayiedu\\User.class");
		boolean isDelete = file2.delete();// 删除旧版本的class文件
		if (!isDelete) {
			System.out.println("热部署失败.");
			return;
		}
		file1.renameTo(file2);
		System.out.println("update success!");
		loadUser();
	}

	public static void loadUser() throws ClassNotFoundException, InstantiationException, IllegalAccessException,
			NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
		MyClassLoader myLoader = new MyClassLoader();
		Class<?> class1 = myLoader.findClass("com.test.User");
		Object obj1 = class1.newInstance();
		Method method = class1.getMethod("add");
		method.invoke(obj1);
		System.out.println(obj1.getClass());
		System.out.println(obj1.getClass().getClassLoader());
	}
}

最後に、私の記事をよく読んでくださった皆様に感謝申し上げます。ファンの増加と注目度を見ると、常に一定の礼儀が存在します。それほど価値のあるものではありませんが、使用できる場合は直接受け取ることができます!

ソフトウェアテスト面接ミニプログラム

何百万人もの人々が使用しているソフトウェア テストの質問バンクです。誰が知っているのか!インターネット上で最も包括的な面接テスト ミニ プログラムです。携帯電話を使用して質問に答えたり、地下鉄やバスに乗ったり、試験に参加したりすることができます。

次のインタビューの質問セクションをカバーします。

1. ソフトウェアテストの基礎理論、2. Web、アプリ、インターフェース機能テスト、3. ネットワーク、4. データベース、5. Linux

6. Web、アプリ、インターフェイスの自動化、7. パフォーマンス テスト、8. プログラミングの基本、9. 時間面接の質問、10. 公開テストの質問、11. セキュリティ テスト、12. コンピューターの基本

情報の入手方法:

おすすめ

転載: blog.csdn.net/myh919/article/details/132713968