Class Loaderメカニズム

6008 ワード


JAVAが起動した後、JVM各級クラスクラスクラスクラスのLoaderを通して、各クラスをメモリにロードします。ローディング過程をもっと理解するために、簡単なクラスLoaderを分析して書いて、その原理を分析します。
JVMのClass Loaderは3階に分けられ、それぞれBootstrap Class Loader、Extension Class Loader、System Class Loaderであり、彼らはクラス継承の親子関係ではなく、論理的な上下関係である。
Bootstrap Class Loaderは、C++で作成され、%jre%/ libディレクトリからクラスをロードしたり、運転時に-Xboott classpathで指定されたディレクトリをロードします。
Extension Class Loaderは拡張類キャリアであり、%jre%/ lib/extディレクトリからクラスをロードするか、または実行時に-Djava.ext.dirsでカタログを作成してロードします。
System Class Loaderは、システム環境変数の設定されたクラスパスから、環境変数の中の現在のディレクトリを表します。実行時-classpathまたは-Djava.class.pathで指定されたディレクトリを通じてクラスをロードします。
 
一般的にカスタムされたClass Loaderはjava.lang.lass Loaderから継承され、異なるクラスのクラスをclassloaderにロードされます。彼らはメモリでも同じではありません。つまり、お互いに変換できなくて、直接に異常を投げます。java.lag.lassLoaderのコアローディング方法はloadClass方法である。
protected synchronized Class<?> loadClass(String name, boolean resolve)

         throws ClassNotFoundException

    {

         // First, check if the class has already been loaded

         Class c = findLoadedClass(name);

         if (c == null) {

             try {

                   if (parent != null) {

                       c = parent.loadClass(name, false);

                   } else {

                       c = findBootstrapClass0(name);

                   }

             } catch (ClassNotFoundException e) {

                 // If still not found, then invoke findClass in order

                 // to find the class.

                 c = findClass(name);

             }

         }

         if (resolve) {

             resolveClass(c);

         }

         return c;

    }
 
上のロードプロセスを通じて、私たちはJVMがデフォルトで両親の委託ローディング機構であることを知ることができます。つまり、先にキャッシュにローディングされているクラスがあるかどうかを判断します。キャッシュがない場合、親ガジェットがある場合、親ガジェットをローディングさせます。父ガジェットが存在しない場合、Bootstrap Class Loaderをローディングさせます。
      両親の委託メカニズムの役割は、システムjarパッケージがローカルに取り換えられないようにすることです。検索方法は一番下の階から探すためです。 したがって、一般的に私たちがカスタムしたクラスリーダーはこのような機構を採用しなければなりません。私たちはjava.lang.lassLoaderを継承してfindclassを実現すればいいです。もっと制御が必要ならば、カスタムのclassloaderはloadClass方法を書き換える必要があります。
      各クラスのLoaderが同じクラスをローディングした後、彼らは互いに待ちません。これは複数のClass Loaderに関連し、現在のスレッドのオンライン文を通じてClass Loaderを取得した後、特に注意が必要です。スレッドのset Contact Class Loaderを通じて、Class Loaderスレッドのコンテキストを設定して、その後、Threadurrent Thread()を通過します。現在のスレッドに保存されているクラスリーダーを取得します。しかし、カスタムクラスのファイルは、Bootstrap Class Loaderにカタログをロードして、Bootstrap Class Loaderにロードされません。ブートクラスのキャリアとして、自分の知らないjarパッケージをロードしません。そして、クラスファイルはjarパッケージに包装してキャリアにロードされるルートディレクトリに入れなければなりません。
 
Bootstrap Class Loader、Extension Class Loader、App Class Loaderの3つの関係は以下の通りです。
Bootstrap Class LoaderはExtension Class Loaderのparentで、Extension Class LoaderはApp Class Loaderのparentです。
しかし、これは関係を継承するのではなく、意味上の定義だけで、基本的には、各クラスのLoaderが実現し、いずれもPart Class Loaderがあります。
 
Class Loaderのget Partメソッドを通じて、現在のClass Loaderのparentを得ることができます。Bootstrap Class Loaderは特別です。java classではないので、Extension Class Loaderのget Partent方法で帰ります。NULLです。
 
いくつかの特殊な需要のため、私達はクラスのLoaderのローディング行為をカスタマイズしなければならないかもしれません。この時はカスタムクラスのLoaderが必要です。
Class Loaderをカスタマイズするには、Class Loaderの抽象的なクラスを引き継ぎ、findClassの方法を書き換える必要があります。この方法は、Class Loaderがクラスを検索する方式を定義しています。
主に拡張できる方法は:
findClass         クラスを検索する方法を定義します。
defineClass      クラスファイルのバイトコードをjvmのクラスにロードします。
findResource   リソースを検索する方法を指定します。
 
既存のクラス・Loaderの実装には以下のようなものがあります。
  • java.net.URLClassLoader
  • java.security.SecureClassLoader
  • java.rmi.server.RMIClassLoader
  • sun.applet.AppletClassLoader
  • Extension Class LoaderとApp Class Loaderはいずれもjava.net.URLClass Loaderのサブクラスです。
    これはURLClassLoaderの構造方法です。
    
    public URLClassLoader(URL[] urls, ClassLoader parent)
    
    public URLClassLoader(URL[] urls)
    
     
    urlsパラメータはローディングが必要なクラスパスパス・url配列で、parent Class Loaderを指定できます。指定しないとデフォルトでは現在のコールクラスのクラスのクラスClass Loaderをparentとします。
    クラスリーダーがクラスをローディングするのは全体的に委託機構を担当するからです。全体として責任を負うということは、一つのクラスのリーダーがクラスをローディングする時に、このクラスが依存していると引用しているすべてのクラスもこのクラスのloaderによってロードされます。
    だから、私達がカスタマイズしたclassloaderがcompany.MyClassをローディングした後、MyClassの中のすべての依存するクラスはこのclass Loaderによってローディングされます。
     
     
     
    Class.forName()とload.loadClass()の違い:
     
    Class.forName(「x.xx」)はクラス.forName(「x.xx」、true、CALLClass.get Class.get Class Loader()に相当し、第二のパラメータ(boll)はクラスをロードする時にこのクラスを初期化するかどうか、すなわち呼び出しクラスの静的ブロックのステートメントおよび初期化静的メンバー変数です。
    Class Loader loader=Thread.current Thread.get Contact Class Loader()//使ってもいいです。
    Class cls=loader.loadClass(「x.xx」);この文は初期化を実行していません。実はClass.forName(「x.xx」、false、loader)と一致しています。ただloader.loadClass(「x.xx」)が実行しているのはもっと低いレベルの操作です。
    クラスを初期化することができるのは、cls.NewInstance()を実行するだけです。このクラスの一例が得られます。
     
      Classの搭載は、The Java Language Specificationの12.2、12.3、12.4の3段階に分けられています。Class.forNameは実際にClass.forNameを呼び出します。注意2番目のパラメータは、クラスがloadingされた後に初期化されなければならないかどうかです。Class Loader.loadClassは実際にClass Loader.loadClassで呼び出されています。二つ目のパラメータは、Classがlinkされているかどうかを示しています。違いが出ます。Class.forName搭載のクラスはすでに初期化されていますが、Class Loader.loadClass搭載のクラスはまだlinkされていません。forNameは配列タイプをサポートしています。loadClassは配列をサポートしていません。
      この二つの方法は効果が同じで、クラスを搭載することができます。ただし、プログラムがクラスに依存して初期化されるかどうかは、クラス.forNameを使用しなければなりません。例えば、JDBCプログラミングでは、Class.forName(「comp.mysql.jdbc.Driver」)が、get Class().get Class Loader().loadClass(「comp.mysql.jdbc.Driver」)に変えたらダメです。なぜですか?comp.mysql.jdbc.Driverのソースコードを開けてみてください。
    //
    // Register ourselves with the DriverManager
    //
    static {
      try {
        java.sql.DriverManager.registerDriver(new Driver());
      } catch (SQLException E) {
        throw new RuntimeException("Can't register driver!");
      }
    }
     
      元々、Driverはstaticブロックに自分でjava.sql.DriverManagerに登録します。staticブロックはクラスの初期化で実行されます。だからこの場所はクラス・forNameしか使えません。