JVMシリーズのようなローディング機構------

Javaのクラスローディング機構

   私たちは、Javaの、Javaソース・ファイルは、任意の言語バインディングを含む仮想マシン、JavaのJava仮想マシンではなくの上に直接実行することはできません、それを知っている、とだけ「クラス・ファイル」に関連することには、この特定のバイナリファイルあなたは上記の仮想マシンで実行する場合は言語、それは、ファイルの.classファイル形式にメモリにクラスファイルからロードされたデータのクラスを記述するための仮想機会をコンパイルする必要があります。
Javaソースファイルが実行された場合は、他の言葉で、それは次のプロセスを通過します:
                                                               

   このプロセスのメモリへの負荷.classファイルへの仮想マシンの場合、クラスのクラスファイルと同じ名前がある場合は、クラスファイルがどのように負荷に、クラスファイル形式が正しくない場合は、仮想マシンが正常にロードすることができ、修正、およびされている場合それは?

   仮想マシンのクラスローディング機構はどのようなものです:

Java型成形仮想マシンのメモリにクラスファイルからロードされたクラスを記述し、データを検証するデータ、及び初期化を解析変換は、仮想マシンの仮想マシン、クラスローディング機構に直接使用することができます。

   Java言語では、負荷、接続のタイプは、初期化は、この戦略は、保証プログラムのための高度な柔軟性を提供し、Java言語のダイナミックな展開で、プログラムの実行中に行われます。たとえば、Javaの多型で指定された実際のインタフェースの実装クラスを実行時まで待機を具現化。

   主に次の段階を介して仮想マシンにロードからのクラス:

                                                               

ロード

「ロード」ロード・フェーズでは、仮想マシンは、次の3つの手順が必要ですが、クラスローディングプロセスの一部です:

  • クラスの完全修飾名で取得したようなバイナリストリーム。
  • これは、ランタイムデータ構造領域法に静的記憶構造のバイナリストリームを表します。
  • メモリ内の発生は、このクラスの各種データ入力領域にアクセスするための方法として、このクラスのjava.lang.Classオブジェクトを表します。

   仮想マシンのロード・フェーズは、それが我々は、次のシナリオのように多くの場所からのバイナリストリームをロードすることができ、そこから私たちのバイナリストリームをロードする必要があることを指定していない、非常にオープンになるように設計されています。

  • 我々JAR、WARおよび他の形態として、ZIPパッケージから読みます。
  • ネットワークからの取得。
  • 例えば、JSPファイルのために、我々は、JSPファイルは、対応するクラスのクラスを生成します書きます。
  • ランタイムダイナミック・エージェント技術の生成、例えばProxyクラス名は、プロキシ$マークが生成されます。

第二に、接続

接続は、次の3つのフェーズ、検証、準備、解像度に分かれています。

確認してください1.

   Java仮想マシンが確認するために、バイトストリームをロードされていない場合は、バイトストリームが崩壊にシステム原因有害ベンを搭載したので、それは可能性があり、負荷がコンパイラまたは正しい形式で生成されることを保証するために、任意の文書を必要としません。または他のセキュリティ問題を解決。これらの潜在的な問題については、Java仮想マシンはクラスが必要な制約を満たすことを確認するために、独自のファイルを必要とします。各ファイルは、必要な制約を満たしているかどうかをリンク時にJava仮想マシンの実装クラスの確認。そしてそれからのいくつかの側面を確認?検証ファイル形式、メタデータ検証、バイトコード検証、検証基準シンボル:ビューの仮想マシンの仕様点は、おそらく、検証フェーズは、4つの段階に分けられます。

1)ファイル形式の検証
この段階では、仕様クラスバイトストリームファイルフォーマットに準拠することを検証するために主に、というように処理することができ、仮想マシンの現在のバージョンかどうか。クラスは、この崇高ようなツールを使用してファイルを開きます。

cafe babe 0000 0034 0029 0900 0b00 1b0a
000c 001c 0700 1d0a 0003 001c 0800 1e0a
0003 001f 0900 0b00 200a 0003 0021 0800
220a 0003 0023 0700 2407 0025 0100 046e
616d 6501 0012 4c6a 6176 612f 6c61 6e67
2f53 7472 696e 673b 0100 0361 6765 0100
0667 6574 4167 6501 0014 2829 4c6a 6176
612f 6c61 6e67 2f53 7472 696e 673b 0100
0443 6f64 6501 000f 4c69 6e65 4e75 6d62
6572 5461 626c 6501 0006 7365 7441 6765
0100 1528 4c6a 6176 612f 6c61 6e67 2f53
......

   4バイトの先頭にマジックナンバーと呼ばれる、ファイルが受け入れられた仮想マシンを検証するために使用することができ、メジャーバージョン番号とマイナーバージョン番号の後ろに4バイト、直ちに所有、バージョン番号は、定数プールのエントリが続いていますように、クラスファイルの種類に関するこの部分は、ここではそれらを繰り返さないため。
ステージは、次の分野の内容を検証します:

  • 開始0xCAFEBABEのマジックナンバーかどうか。
  • メジャーとマイナーバージョン番号のかどうか、現在の仮想マシンの処理範囲内で、
  • 定数プール内の定数によってサポートされていない一定のタイプがあります。
  • 定数または定数の不存在下で様々な定数プールポインティングに指標値点は、型を満たしていない場合。
  • CONSTANT_Utf8_infoタイプは定数UTF8エンコードされたデータが満たしていないされている。
    ...
    これだけのステージによる検証の後、バイトストリームは、ストレージ用メモリのメソッド領域を入力します。

2)メタデータの検証

:このフェーズは、次のような仕様のJavaのメタデータ情報が満たしていないかどうかを確認するために情報のバイトコード意味解析に記述されています

  • クラスは親クラスを持っているかどうか。
  • このクラスの親を継承したクラスを継承することはできませんかどうか。
  • 抽象クラスまたはインタフェースの実装要件に応じた方法を達成するために必要な場合には、
    ...

①バイトコード検証

   このステージは、主にデータ・フローおよび制御フロー解析を介して行われ、法的論理的であるセマンティック・プロシージャを定義し、仮想マシンへの害は発生しません。例えば:ローディングのタイプは、ローカル変数テーブルの種類と上体法以外の命令のバイトコード命令にジャンプしないオペランドスタック矛盾を保証するものではありません。

②シンボル検証は、
   直接参照を満たすために仮想機会は、変換、分析段階で行われるこの変換アクションを引用しました。検証は通常、文字列記述子の参照、メソッド、フィールド、プロパティ、など、アクセスを制限するかどうかに応じて対応するクラスを見つけることが必要です。
java.lang.IllegalAccessError、java.lang.NoSuchMthodError、java.lang.NoSuchFiledErrorその他の異常:によって参照の準拠を検証することができない場合にスローされるように、正常な動作を保証するために、解決のシンボリック参照の検証は、実行することができます

2、準備ができて

   準備フェーズは、クラス変数にメモリを割り当てることで、デフォルト値に初期化します。可変メモリ割り当てに準備段階でのみ参照すると、クラスのインスタンス変数の変数を含まない、インスタンス変数は、ヒープオブジェクトのインスタンスと一緒に割り当てられます。そして、変数の割り当ては、デフォルト値のみを初期化します、例えばません:static int value = 2;最初の準備段階後の初期値は、初期化時間が動作するまでの割り当て操作が待機する代わりに、2のそのint型0のデフォルト値です。

####図3は、解決する
   この段階は、直接参照話すシンボリック参照を変換する処理です。だから、シンボリック参照は何ですか?コンパイル時にJavaクラスは、Javaクラスは、それが唯一の代わりにシンボリックリファレンスを使用することができ、実際のメモリアドレス参照先のクラスを知りません。定数のクラスファイルそれCONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info他の種類が表示され、実際には、クラスやインタフェース、名前と記述子、メソッド名と記述子フィールドの完全修飾名。ファイルをロードするときに、直接参照の実アドレスでライン内の参照を置き換えますクラスのJVM。解決され、この段階で解析されたクラスまたはインタフェースは、メソッドのような解析、フィールドを解析し、あります

第三に、初期化

   最後のステップは、このフェーズは、クラスコンストラクタ<clinit>()メソッド、クラス変数の実際の割り当ての実装になり、クラスのロードを初期化することです。静的変数の親クラスの割り当ては、サブクラスに優先権を与えることを意味します実行のサブクラスに優先して親クラス<CINIT>()メソッド、ことを確実にする仮想機会。クラス変数の初期化は、次の順序で行うことになります。
親クラスがロードされていない場合、親クラスの静的コード・ブロックと静的変数の最初の実装は初期化され、表示されていることを静的コードブロックの実行順序と静的変数は、逐次コードと関連しています- ---「コードと静的初期化のサブクラス静的ブロックを行います。----コンストラクタを実行サブクラス「サブクラスのインスタンス変数を初期化----行う」----親クラスのコンストラクタが実行を「親インスタンス変数は-----初期化実行」。
仮想マシンの仕様厳格なルールであり、クラスの唯一の5例は、すぐに「初期化」する必要があります。

(1)新しい、getstatic、putstatic invokestatic又は4このバイトコード命令に遭遇するクラスが初期化されていない場合、初期化を開始する必要があります。

(2)コールを反映する方法基づいjava.lang.reflectのパッケージを使用する場合、クラスが初期化されていない場合、それは初期化を開始する必要があります。

(3)クラスを初期化するとき、その親の初期化が発見されていない場合、あなたは親クラスの最初のトリガ初期化する必要があります。

仮想マシンの起動は、ユーザのニーズは(そのクラスのmain()メソッドを含む)メインクラスの実行、このマスタークラスを初期化する仮想マシンを指定する(4)。

(5)場合、動的言語サポートJDK1.7解析結果java.lang.invoke.MethodHandle REF_getStatic、REF_putStatic、REF_invokeStatic方法ハンドル、およびこの方法ハンドルクラスが初期化されていない対応する最後の例であれば、あなたは、その初期化をトリガーする必要があります。

クラスローダ

   クラスのロード処理の前の分析、あなたがそのようなクラスが仮想マシンにロードされている必要があり、クラスローダは、この操作の実行です。クラスローダは、オブジェクトのjava.lang.Classのインスタンスは、すべてのクラスのためにメモリにロードされて生成したすべてのクラスをロードする責任があります。クラスがJVMにロードされると、クラスが再ロードされません。それでは、どのようにJavaのクラスが実装または二回負荷がそのクラスをロードされていないことを確認するには?両親は委任モデルを使用すること。

Javaでは、クラスローダは三種類に分けることができます。

  • スタートクラスローダ(ブートストラップクラスローダ)
    など、このクラスローダは、JAVA_HOME \ libディレクトリにまたはXbootclasspathオプションを指定してjarファイルパッケージライブラリに責任があるが、ブートストラップクラスローダのために、仮想マシンのメモリにロードされた仮想マシンのローカル実装の詳細に来ます、開発者は、その参照によって直接操作することはできません、ブートクラスローダの参照を直接取得することはできません
  • 拡張クラスローダ(拡張クラスローダ)
    このローダーは、次のJAVA_HOME \ libに\ extディレクトリまたは-Djava.ext.dirは、ライブラリ内の場所を指定したシステム変数をロードする責任があるsun.misc.Launcher $ ExtClassLoader、によって実装されます。
  • アプリケーションクラスローダが(Appliactionクラスローダ)
    アプリケーションのクラスローダはまた、開発者が独自のクラスローダをカスタマイズすることができ、ユーザーのパス上の指定されたライブラリをロードする責任があるシステムクラスローダとして知られ、そうでない場合は、独自のカスタムクラスローダデバイスは、デフォルトでローダーを使用しています。
両親は、クラスローディング機構を委任します

   Javaの親が同じクラスが二回ロードされることはありません確実にするために委譲モデルを使用し、それが動作:クラスがロードされると、要求を受信すると、すぐに負荷に行くが、それに委託しません負荷への親クラスローダ、親クラスローダ場合、まだ親ローダーがありますが、順次アップ委員会に継続し、親クラスローダがクラスローディングのタスクを完了することができれば、再帰的に要求は最終的に、ブートクラスローダの上部に到達します成功リターン、負荷への親クラスローダは、このタスクを完了できない場合は、子ローダーが自分をロードしようとする、これは両親の代理人モードです。

Javaの関係ブートクラスローダ、拡張クラスローダー、クラスローダのアプリケーションでは以下のように:

                                                               

親クラスローダシステムクラスローダーがクラスローダを拡張することで、親クラスローダクラスローダは、拡張ブートクラスローダーです

   使用親委任モデル、あなたはクラス内クラスローダインスタンスのrt.jarのをロードしたいのかに関係なく、最終的にはこのように、最も基本的なJavaの型システムの動作を保証する、クラスローダの上部に開始するように任命されます。ほとんどの場合、上位クラスのベースは、ローダによってロードされました。
両親は論理モデルを委任し、この規範の下で一緒に次のように分析し、非常に明確です:

 synchronized (getClassLoadingLock(name)) {
            // 检查该类是否已经被加载
            Class c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
					// 如果存在父类加载器的话,使用父类加载器加载
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
						// 说明父类是BootStarp加载器,若在bootstrap class loader的查找范围内没有查找到该类,则返回null   
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // 如果依然找不到的话,则调用本身(这个本身包括ext和app)的
					 // findClass(name)来查找类,若自身也加载不了,会产生ClassNotFoundException异常并向上抛出
                    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.lang.Classクラスのインスタンスがキャッシュされます。次回はクラスローダは、クラスの直接キャッシュされたインスタンスを使用しますが、再ロードしようとしていない時にクラスをロードするための要求。これは、クラスローダインスタンスのために、クラスの同じフルネームは、loadClassメソッドが繰り返し呼び出されることはありませんされ、一度だけロードされています。
   ユーザーは、自分のクラスローダを定義することができ、あなただけのライン上にfindClassメソッドを書き換える必要があります。一般的にはloadClassを既存の上書きしないようにしてください(...)論理的な方法を委任、これは何らかの複雑なロジックに正常に動作しない場合がありますが、デフォルトのクラスローダにシステムの原因となります。defineClassを呼び出すことにより、クラスを完了するために行われる実際の作業をロードし、それはClassオブジェクトを返します。

ここでは、カスタムクラスローダの例を見て:

public class UserClassLoader extends ClassLoader {

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {


        File file = new File("G:\\Person.class");

        byte[] bytes = new byte[0];
        try {
            bytes = getClassBytes(file);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //defineClass方法可以把二进制流字节组成的文件转换为一个java.lang.Class
        Class<?> c = this.defineClass(name, bytes, 0, bytes.length);
        return c;
    }

    private byte[] getClassBytes(File file) throws IOException {
        FileInputStream fis = new FileInputStream(file);
        FileChannel fc = fis.getChannel();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        WritableByteChannel wbc = Channels.newChannel(baos);
        ByteBuffer by = ByteBuffer.allocate(1024);
        while (true){
            int i = fc.read(by);
            if (i == 0 || i == -1)
                break;
            by.flip();
            wbc.write(by);
            by.clear();
        }
        fis.close();
        return baos.toByteArray();
    }
}

A人は、クラスファイルにコンパイルする必要があります

public class Person {

    private String name;
    private String age;

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age='" + age + '\'' +
                '}';
    }
}
public class ClassLoaderTest {

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        UserClassLoader userClassLoader = new UserClassLoader();
		// 类的全限定名
        Class<?> clazz = Class.forName("classloader.Person", true, userClassLoader);
        Object obj = clazz.newInstance();

        //打印出我们的自定义类加载器
        System.out.println(obj.getClass().getClassLoader());
    }

}

   「java.langで」クラスの始まりと負荷にdefinClass()メソッドを持つ独自のカスタムクラスローダが成功しないにもかかわらず、それを注意してください。「javaの」これはにクラスが始まっている場合はjava.lang.ClassLoader preDefineClassメソッドの内部では、それはSecurityExceptionをスローしますので、ソースコードの一部を以下に示します。

if ((name != null) && name.startsWith("java.")) {
            throw new SecurityException
                ("Prohibited package name: " +
                 name.substring(0, name.lastIndexOf('.')));
        }

仮想マシンのクラスローディング機構は繰り返し荷重のようなものを避けるが、回避JavaのコアAPIが改ざんされているとも、効果的にJavaプログラムの安定した動作が保証します。

参考図書:

Java仮想マシンの深い理解 - 周志明と

发布了8 篇原创文章 · 获赞 0 · 访问量 12

おすすめ

転載: blog.csdn.net/m0_46213308/article/details/104058875