关于ClassLoader的知识点记录

JVM 启动的时候会产生3个ClassLoader,他们分别负责加载不同目录下的class。其中Bootstrap ClassLoader 是 C++ 实现,如果 parent ClassLoader是它的话,获取的时候是会返回null 的。其中 System ClassLoader 也就是我们所说的 AppClassLoader。

  1. Bootstrap ClassLoader/启动类加载器
    主要负责jdk_home/lib目录下的核心 api 或 -Xbootclasspath 选项指定的jar包装入工作。
  2. Extension ClassLoader/扩展类加载器
    主要负责jdk_home/lib/ext目录下的jar包或 -Djava.ext.dirs 指定目录下的jar包装入工作。
  3. System ClassLoader/系统类加载器
    主要负责java -classpath/-Djava.class.path所指的目录下的类与jar包装入工作。
  4. User Custom ClassLoader/用户自定义类加载器(java.lang.ClassLoader的子类)
    在程序运行期间, 通过java.lang.ClassLoader的子类动态加载class文件, 体现java动态实时类装入特性。

关于类加载顺序是 采用“双亲委派”加载链模式的。用户自定义的 ClassLoader 是通过继承 抽象ClassLoader 来实现。

这种加载机制可以避免重复加载并保障java核心类的加载安全性。

 

而线程上下文加载器 则可以打破这种 “双亲委派”加载链模式。

 

每个线程都有一个关联的上下文Classloader。如果使用new Thread()方式生成新的线程,新线程将继承其父线程的上下文Classloader。如果程序对线程上下文Classloader没有任何改动的话,程序中所有的线程将都使用System Classloader作为上下文Classloader。

 

当使用Thread.currentThread().setContextClassLoader(classloader)时,线程上下文 Classloader就变成了指定的Classloader了。此时,在本线程的任意一处地方,调用 Thread.currentThread().getContextClassLoader(),都可以得到前面设置的Classloader。

 

回到JAXP的例子,假设xercesImpl.jar只有AClassLoader能装载,现在A.class内部要使用JAXP,但是A.class却不是由AClassLoader或者它的子Classloader装载的,那么在A.class中,应该这样写才能正确得到xercesImpl的实现:

AClassLoader aClassLoader = new AClassLoader(parent);
Thread.currentThread().setContextClassLoader(aClassLoader);
SAXParserFactory factory = SAXParserFactory.getInstance();
...

 

JAXP这时就可以通过线程上下文Classloader装载xercesImpl的实现类了,当然,还有一个前提是在配制文件或启动参数中指定了使用xerces作为JAXP的实现。下面是JAXP中的代码片断:

ClassLoader cl = Thread.currentThread().getContextClassLoader();
…
Class providerClass = cl.loadClass(className);

 

 

Java默认的线程上下文类加载器是 系统类加载器(AppClassLoader)。

使用线程上下文类加载器, 可以在执行线程中, 抛弃双亲委派加载链模式, 使用线程上下文里的类加载器加载类.
典型的例子有, 通过线程上下文来加载第三方库jndi实现, 而不依赖于双亲委派.
大部分java app服务器(jboss, tomcat..)也是采用contextClassLoader来处理web服务。
还有一些采用 hotswap 特性的框架, 也使用了线程上下文类加载器, 比如 seasar (full stack framework in japenese).

http://tomcat.apache.org/tomcat-5.5-doc/class-loader-howto.html  这里是关于tomcat 容器的类加策略。

http://www.javaworld.com/javaqa/2003-06/01-qa-0606-load.html  这里是一片关于 java Context Loader的应用说明。

 

static块在什么时候执行?

  • 当调用forName(String)载入class时执行,如果调用ClassLoader.loadClass并不会执行.forName(String,false,ClassLoader)时也不会执行.
  • 如果载入Class时没有执行static块则在第一次实例化时执行.比如new ,Class.newInstance()操作
  • static块仅执行一次

各个java类由哪些classLoader加载?

  • java类可以通过实例.getClass.getClassLoader()得知
  • 接口由AppClassLoader(System ClassLoader,可以由ClassLoader.getSystemClassLoader()获得实例)载入
  • ClassLoader类由bootstrap loader载入

类装载的方式

类装载的方式主要有两种:显式的和隐式的。

  • 显式类装载

    发生在使用以下方法调用进行装载类的时候:

    • ClassLoader.loadClass()(使用指定的Classloader进行装载)

    • Class.forName()(使用当前类的Caller Classloader进行装载)

      当调用上述方法的时候,指定的Class(以类名为参数)由Classloader装入。这两个方法的行为有轻微的区别,Class.forName()在类装载完成后,会对类进行初始化,而ClassLoader.loadClass()只负责装载类。

  • 隐式类装载

    发生在由于引用、实例化或继承导致需要装载类的时候。隐式类装载是在幕后启动的,JVM会解析必要的引用并装载类。

    类的装载通常组合了显式和隐式两种方式。例如,Classloader可能先显式地装载一个类,然后再隐式地装载它引用的其它类。

  • 类装载发生的时间

    从类装载方式的描述中我们可以看到,只有在显式的调用方法或者实例化、引用、继承一个类时,类才真正被装载。由此,我们可以知道,import并不会导致类装载,以及,在一个类实例化之前,调用它的静态方法,会导致这个类和它的父类、实现的接口和相关的静态成员的类会被装载,而它的成员变量的类却不会被装载

父装载器不能够引用子装载器Class,

 

JVM中类的唯一性

JVM 为每一个Classloader维护一个唯一标识。在一个JVM里(对应一个Java进程),可以由不同的Classloader装载多个同名的类(指包名和类名都完全相同,下同),为了唯一地标识被不同Classloader装载的类,JVM会在被装载的类名前加上装载该类的Classloader的标识。

 

 

摘抄 :

http://blog.chenlb.com/2009/06/java-classloader-architecture.html

http://infocenter.apusic.com/help/index.jsp?topic=/aas/v6/ch27s01.html

 

 

 

你可能感兴趣的:(关于ClassLoader的知识点记录)