Java中のクラスのキャリア。動力ノードJava学院の整理
javaの動的性からクラスローディング機構まで
Javaは動的言語である。この「動き」はどう理解しますか?あるいは一つの言語にはどのような特徴がありますか?javaについては、私はこのように理解しています。
JVM(java仮想マシン)は、ローカルマシンコード命令ではなく、バイトコードと呼ばれる命令(クラスファイルに存在する)を実行します。これは、仮想マシンがバイトコードを実行する前に、関連するクラスファイルをメモリにロードする必要があります。仮想マシンは必要なすべてのクラスファイルを一度にロードするのではなく、実行する時にどのクラスのファイルを使うか全然分かりません。クラスを使うたびに、このクラスに関連するクラスファイルを動的にロードします。これがJavaが動的言語と呼ばれる根本的な原因です。ダイナミックローディングクラスに加えて、ダイナミックな初期化クラスがあり、クラスを動的にリンクします。動的初期化と動的リンクは他の文章において紹介されます。本論文ではクラスのローディングのみに関心があります。
JVMでクラスのローディングを担当しているのは、ここで紹介するクラスローダーです。したがって、クラスローダはJVMになくてはならない重要なコンポーネントです。
javaのクラスのキャリアとクラスのキャリアの動作原理
javaには三つのキャリアがあります。各クラスのキャリアは、作成時にすでに彼らの対応するディレクトリを指定しています。つまり、各クラスのキャリアはどこに行くかが確定しています。Class Loaderクラスでは、getTarget Path()などの方法があると思います。彼らが対応する経路を得て、jdkの文書を探してみました。以下はこの3種類のキャリアと彼らが対応する経路です。
* AppClass Loader -- クラスパスで指定されたクラスをロードします。
* Ext Class Loader -- jre/lib/extディレクトリまたはjava.ext.dirsシステム属性が定義するディレクトリの下のクラスをロードします。
* BootStrap -- JRE/lib/rt.jarのクラスをロードします。
では、このようなキャリアはどのように働きますか?jdkのClass Loader類のソースコードを参照することができます。このクラスの実装は、まずクラスをロードするために、loadClass方法を使用して、クラスファイルのデータを読み込み、戻ります。findClass方法を使用して、DefineClass方法を呼び出します。戻りデータを仮想マシンの実行時に識別できるタイプ情報に加工します。だから、私達は自分のクラスのキャリアを開発するなら、jdkのクラスクラスのクラスを受け継ぐだけでいいです。そして、findClassの方法をカバーすればいいです。残りは仕事です。父は完成します。他のjavaプラットフォームには、Javaeeプラットフォームのtomcatサーバー、Androidプラットフォームのdalvik仮想マシンなど、自分の特定の種類のキャリアを実現しているものがあります。
仮想マシンのローディングには2つの方法があります。一つの方法は上述のClass Loader.loadClass()方法で、もう一つは反射API、Class.forName()方法を使っています。実はClass.forName()メソッドの内部でも使用されているクラスLoaderです。ClassクラスにおけるforName方法の実現は以下の通りである。
クラスのキャリアには三つの特性があり、それぞれ委任、視認性と単一性があり、他の文章ではこの三つの特性について以下のように紹介しています。
* 委託メカニズムとは、1つのクラスをロードする要求を親のキャリアに渡すことです。この親のキャリアが見つからない場合、またはこのクラスをロードすることができます。
* 視認性の原理としては、サブクラスのキャリアは、すべての親タイプのキャリアのクラスを見ることができますが、親タイプのキャリアは、サブキャリアのクラスをロードすることができません。
* 単一の原理とは、1つのクラスを1回だけロードすることです。これは、サブキャリアが親のキャリアを再びロードしないように委託機構によって確認されたクラスです。
その中で、委任メカニズムは基礎であり、他の資料でもこのような仕組みを類荷重器の両親に委任するモデルといいますが、実は同じ意味です。加算可能性と単一性は、委任メカニズムに依存する。
以下のコードテストクラスのキャリアの委任メカニズム:
システムクラスのキャリアとスレッドのコンテキストクラスのキャリア
javaには、システムクラスのキャリアとスレッドのコンテキストタイプのキャリアという2つの概念が存在する。
実はシステムクラスのキャリアはアプリクラスのキャリアです。二つは同じキャリアに値するので、以下のコードは検証できます。
各スレッドにはコンテキストタイプのキャリアがあります。スレッド実行時に使用するクラスをロードするため、デフォルトでは親スレッドのコンテキストタイプのキャリア、つまりAppClass Loaderです。
スレッドに特定のクラスキャリアを設定することもできます。このようにすると、スレッドは実行時にこの特定のクラスキャリアを使用して、使用したクラスをロードします。次のコード:
クラスのキャリアの可視性
次のようなキャリアの視認性を検証します。すなわち、サブクラスのキャリアは、すべての親タイプのキャリアローディングの種類を見ることができます。一方、親タイプのキャリアは、サブタイプのキャリアローディングの種類を見ることができません。
以下のコードは、親キャリアExt Class Loaderを使用して、サブキャリアApClass Loaderパスのクラスをロードします。出力から分かります。実現することは不可能です。
次のコードは、サブキャリアを使用して、親キャリアBootStrapのクラスをロードします。これは実現できます。
テストコード
ここまでで、クラスのキャリアの知識は全部話し終わりました。以下はテストコード全体です。
Javaは動的言語である。この「動き」はどう理解しますか?あるいは一つの言語にはどのような特徴がありますか?javaについては、私はこのように理解しています。
JVM(java仮想マシン)は、ローカルマシンコード命令ではなく、バイトコードと呼ばれる命令(クラスファイルに存在する)を実行します。これは、仮想マシンがバイトコードを実行する前に、関連するクラスファイルをメモリにロードする必要があります。仮想マシンは必要なすべてのクラスファイルを一度にロードするのではなく、実行する時にどのクラスのファイルを使うか全然分かりません。クラスを使うたびに、このクラスに関連するクラスファイルを動的にロードします。これがJavaが動的言語と呼ばれる根本的な原因です。ダイナミックローディングクラスに加えて、ダイナミックな初期化クラスがあり、クラスを動的にリンクします。動的初期化と動的リンクは他の文章において紹介されます。本論文ではクラスのローディングのみに関心があります。
JVMでクラスのローディングを担当しているのは、ここで紹介するクラスローダーです。したがって、クラスローダはJVMになくてはならない重要なコンポーネントです。
javaのクラスのキャリアとクラスのキャリアの動作原理
javaには三つのキャリアがあります。各クラスのキャリアは、作成時にすでに彼らの対応するディレクトリを指定しています。つまり、各クラスのキャリアはどこに行くかが確定しています。Class Loaderクラスでは、getTarget Path()などの方法があると思います。彼らが対応する経路を得て、jdkの文書を探してみました。以下はこの3種類のキャリアと彼らが対応する経路です。
* AppClass Loader -- クラスパスで指定されたクラスをロードします。
* Ext Class Loader -- jre/lib/extディレクトリまたはjava.ext.dirsシステム属性が定義するディレクトリの下のクラスをロードします。
* BootStrap -- JRE/lib/rt.jarのクラスをロードします。
では、このようなキャリアはどのように働きますか?jdkのClass Loader類のソースコードを参照することができます。このクラスの実装は、まずクラスをロードするために、loadClass方法を使用して、クラスファイルのデータを読み込み、戻ります。findClass方法を使用して、DefineClass方法を呼び出します。戻りデータを仮想マシンの実行時に識別できるタイプ情報に加工します。だから、私達は自分のクラスのキャリアを開発するなら、jdkのクラスクラスのクラスを受け継ぐだけでいいです。そして、findClassの方法をカバーすればいいです。残りは仕事です。父は完成します。他のjavaプラットフォームには、Javaeeプラットフォームのtomcatサーバー、Androidプラットフォームのdalvik仮想マシンなど、自分の特定の種類のキャリアを実現しているものがあります。
仮想マシンのローディングには2つの方法があります。一つの方法は上述のClass Loader.loadClass()方法で、もう一つは反射API、Class.forName()方法を使っています。実はClass.forName()メソッドの内部でも使用されているクラスLoaderです。ClassクラスにおけるforName方法の実現は以下の通りである。
public static Class<?> forName(String name, boolean initialize,
ClassLoader loader)
throws ClassNotFoundException
{
if (loader == null) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
ClassLoader ccl = ClassLoader.getCallerClassLoader();
if (ccl != null) {
sm.checkPermission(
SecurityConstants.GET_CLASSLOADER_PERMISSION);
}
}
}
return forName0(name, initialize, loader);
}
/** Called after security checks have been made. */
private static native Class forName0(String name, boolean initialize,
ClassLoader loader)
throws ClassNotFoundException;
クラスキャリアの三つの特性 クラスのキャリアには三つの特性があり、それぞれ委任、視認性と単一性があり、他の文章ではこの三つの特性について以下のように紹介しています。
* 委託メカニズムとは、1つのクラスをロードする要求を親のキャリアに渡すことです。この親のキャリアが見つからない場合、またはこのクラスをロードすることができます。
* 視認性の原理としては、サブクラスのキャリアは、すべての親タイプのキャリアのクラスを見ることができますが、親タイプのキャリアは、サブキャリアのクラスをロードすることができません。
* 単一の原理とは、1つのクラスを1回だけロードすることです。これは、サブキャリアが親のキャリアを再びロードしないように委託機構によって確認されたクラスです。
その中で、委任メカニズムは基礎であり、他の資料でもこのような仕組みを類荷重器の両親に委任するモデルといいますが、実は同じ意味です。加算可能性と単一性は、委任メカニズムに依存する。
以下のコードテストクラスのキャリアの委任メカニズム:
ClassLoader appClassLoader = ClassLoaderTest.class.getClassLoader();
System.out.println(appClassLoader); //sun.misc.Launcher$AppClassLoader@19821f
ClassLoader extClassLoader = appClassLoader.getParent();
System.out.println(extClassLoader); //sun.misc.Launcher$ExtClassLoader@addbf1
//AppClassLoader ExtClassLoader
System.out.println(extClassLoader.getParent()); //null
//ExtClassLoader null, BootStrap, c
印刷の結果からわかるように、私達が自分で作成したクラスのキャリアをロードするのはAppClass Loaderで、AppClass Loaderの父のキャリアはExt Class Loaderで、Ext Class Loaderの父のキャリアが戻ってきた結果はnullです。これは彼の追加キャリアがBootStrapであることを示しています。このキャリアは仮想マシンと密接に繋がっています。仮想マシンjkで起動します。Cによって実現されました。対応するjavaオブジェクトがないので、nullに戻ります。しかし、論理的には、BootStrapはまだExt Class Loaderの父のキャリアです。つまり、Ext Class Loaderがクラスをローディングするたびに、BootStrapのローディングを依頼します。 システムクラスのキャリアとスレッドのコンテキストクラスのキャリア
javaには、システムクラスのキャリアとスレッドのコンテキストタイプのキャリアという2つの概念が存在する。
実はシステムクラスのキャリアはアプリクラスのキャリアです。二つは同じキャリアに値するので、以下のコードは検証できます。
ClassLoader appClassLoader = ClassLoaderTest.class.getClassLoader();
System.out.println(appClassLoader); //sun.misc.Launcher$AppClassLoader@19821f
ClassLoader sysClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(sysClassLoader); //sun.misc.Launcher$AppClassLoader@19821f
// , ,
これらの2つのクラスのキャリアに対応する出力は、クラス名だけでなく、オブジェクトのハッシュ値まで同じであり、システムクラスのキャリアとアプリケーションクラスのキャリアが同じクラスだけでなく、同じクラスのオブジェクトであることを十分に示している。各スレッドにはコンテキストタイプのキャリアがあります。スレッド実行時に使用するクラスをロードするため、デフォルトでは親スレッドのコンテキストタイプのキャリア、つまりAppClass Loaderです。
new Thread(new Runnable() {
@Override
public void run() {
ClassLoader threadcontextClassLosder = Thread.currentThread().getContextClassLoader();
System.out.println(threadcontextClassLosder); //sun.misc.Launcher$AppClassLoader@19821f
}
}).start();
このサブスレッドは実行時に印刷された情報はsun.misc.Launcherである。AppClassLoader@19821fメインスレッドのApClass Loaderと同じオブジェクト(ハッシュ値が同じ)が見られます。 スレッドに特定のクラスキャリアを設定することもできます。このようにすると、スレッドは実行時にこの特定のクラスキャリアを使用して、使用したクラスをロードします。次のコード:
Thread th = new Thread(new Runnable() {
@Override
public void run() {
ClassLoader threadcontextClassLosder = Thread.currentThread().getContextClassLoader();
System.out.println(threadcontextClassLosder); //jg.zhang.java.testclassloader.ClassLoaderTest$3@1b67f74
}
});
th.setContextClassLoader(new ClassLoader() {});
th.start();
スレッドが実行される前に、匿名の内部クラスのキャリアオブジェクトが設定されています。スレッドが実行される時、出力される情報は、jg.zhang.java.testclassloader.lassLoader Testです。3@1b67f74つまり、私達が設置したその種類のキャリアの対象です。 クラスのキャリアの可視性
次のようなキャリアの視認性を検証します。すなわち、サブクラスのキャリアは、すべての親タイプのキャリアローディングの種類を見ることができます。一方、親タイプのキャリアは、サブタイプのキャリアローディングの種類を見ることができません。
以下のコードは、親キャリアExt Class Loaderを使用して、サブキャリアApClass Loaderパスのクラスをロードします。出力から分かります。実現することは不可能です。
try {
Class.forName("jg.zhang.java.testConcurrent.Person", true,
ClassLoaderTest.class.getClassLoader().getParent());
System.out.println("1 -- ");
} catch (ClassNotFoundException e) {
//e.printStackTrace();
System.out.println("1 -- ");
}
出力は:1--クラスが見つかりません。説明はクラスNotFoundExceptionの異常を投げました。その理由は、ExtClass Loaderにjg.zhang.java.testConcerent.Personというクラスをロードさせることです。このクラスはjre/lib/extディレクトリの下か、java.ext.dirsシステムの属性が定義されているディレクトリの下にないので、クラスNotFoundExceptionをスローします。ですから、父のキャリアは布団の装填器に載せるべき種類を積み込むことができません。つまり、この類は父のキャリアには見られないということです。この機構は委任機構に依存する。 次のコードは、サブキャリアを使用して、親キャリアBootStrapのクラスをロードします。これは実現できます。
try {
Class.forName("java.lang.String", true,
ClassLoaderTest.class.getClassLoader());
System.out.println("2 -- ");
} catch (ClassNotFoundException e) {
//e.printStackTrace();
System.out.println("2 -- ");
}
出力は:2--クラスがロードされます。Stringクラスのロードに成功しました。AppClass LoaderによってStringクラスをロードするよう指定した場合、AppleクラスLoaderからBootStrapにロードされるからです。子キャリアの父ガジェットによってロードされたが、父ガジェットによってロードされたクラスは子キャリアにとって見られるとも言える。これは同様に委任機構に依存する。実際には、仮想マシンの起動初期には、java.lang.StringはすでにBootStrapにプリロードされていました。この時、再びローディングされていることが分かりました。このことはまた、クラスキャリアの単一性を証明している。 テストコード
ここまでで、クラスのキャリアの知識は全部話し終わりました。以下はテストコード全体です。
package jg.zhang.java.testclassloader;
/**
* ImportNew << >>,
* :http://www.importnew.com/6581.html
*
* Java : 、 。
* , , 。
* , 。
* , 。
*
* : ,
* ClassLoader getTargetPath() , , jdk , .
* AppClassLoader -- classpath
* ExtClassLoader -- jre/lib/ext java.ext.dirs
* BootStrap -- JRE/lib/rt.jar
*
*
*
* @author zhangjg
*
*/
public class ClassLoaderTest {
public static void main(String[] args) {
test1();
test2();
test3();
}
/**
*
*/
private static void test3() {
/**
* 1 , ,
* , AppClassLoader
*/
new Thread(new Runnable() {
@Override
public void run() {
ClassLoader threadcontextClassLosder = Thread.currentThread().getContextClassLoader();
System.out.println(threadcontextClassLosder); //sun.misc.Launcher$AppClassLoader@19821f
}
}).start();
/**
* 2
*/
Thread th = new Thread(new Runnable() {
@Override
public void run() {
ClassLoader threadcontextClassLosder = Thread.currentThread().getContextClassLoader();
System.out.println(threadcontextClassLosder); //jg.zhang.java.testclassloader.ClassLoaderTest$3@1b67f74
}
});
th.setContextClassLoader(new ClassLoader() {});
th.start();
}
/**
* ,
*/
private static void test2() {
/**
* 1 ExtClassLoader jg.zhang.java.testConcurrent.Person
* jre/lib/ext java.ext.dirs
* ClassNotFoundException
*
* ,
*
*/
try {
Class.forName("jg.zhang.java.testConcurrent.Person", true,
ClassLoaderTest.class.getClassLoader().getParent());
System.out.println("1 -- ");
} catch (ClassNotFoundException e) {
//e.printStackTrace();
System.out.println("1 -- ");
}
/**
* 2 AppClassLoader java.lang.String
* ,
* AppClassLoader BootStrap
* , ,
*
* ,java.lang.String BootStrap
* , ,
*/
try {
Class.forName("java.lang.String", true,
ClassLoaderTest.class.getClassLoader());
System.out.println("2 -- ");
} catch (ClassNotFoundException e) {
//e.printStackTrace();
System.out.println("2 -- ");
}
}
/**
*
*/
private static void test1() {
ClassLoader appClassLoader = ClassLoaderTest.class.getClassLoader();
System.out.println(appClassLoader); //sun.misc.Launcher$AppClassLoader@19821f
ClassLoader sysClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(sysClassLoader); //sun.misc.Launcher$AppClassLoader@19821f
// , ,
ClassLoader extClassLoader = appClassLoader.getParent();
System.out.println(extClassLoader); //sun.misc.Launcher$ExtClassLoader@addbf1
//AppClassLoader ExtClassLoader
System.out.println(extClassLoader.getParent()); //null
//ExtClassLoader null, BootStrap, c
}
}
以上が本文の全部です。皆さんの勉強に役に立つように、私たちを応援してください。