java之classloader体系结构

标签: java classloader 体系结构 | 发表时间:2014-03-03 20:08 | 作者:cyjxyx
出处:http://www.iteye.com

原文出处: http://www.iteye.com/topic/136427

jvm classLoader architecture :

a, Bootstrap ClassLoader/启动类加载器
主要负责jdk_home/lib目录下的核心 api 或 -Xbootclasspath 选项指定的jar包装入工作.

 

b, Extension ClassLoader/扩展类加载器
主要负责jdk_home/lib/ext目录下的jar包或 -Djava.ext.dirs 指定目录下的jar包装入工作

 

c, System ClassLoader/系统类加载器
主要负责java -classpath/-Djava.class.path所指的目录下的类与jar包装入工作.

 

b, User Custom ClassLoader/用户自定义类加载器(java.lang.ClassLoader的子类)
在程序运行期间, 通过java.lang.ClassLoader的子类动态加载class文件, 体现java动态实时类装入特性.

 

类加载器的特性:

1, 每个ClassLoader都维护了一份自己的名称空间, 同一个名称空间里不能出现两个同名的类。
2, 为了实现java安全沙箱模型顶层的类加载器安全机制, java默认采用了 ” 双亲委派的加载链 ” 结构.


如下图:
java class loader

Class Diagram:
classloader 类图

类图中, BootstrapClassLoader是一个单独的java类, 其实在这里, 不应该叫他是一个java类。
因为, 它已经完全不用java实现了。

 

它是在jvm启动时, 就被构造起来的, 负责java平台核心库。(具体上面已经有介绍)

启动类加载实现 (其实我们不用关心这块, 但是有兴趣的, 可以研究一下 ):
bootstrap classLoader 类加载原理探索

 

自定义类加载器加载一个类的步骤 :

自定义类加载器加载一个类的步骤

 

ClassLoader 类加载逻辑分析, 以下逻辑是除 BootstrapClassLoader 外的类加载器加载流程:

 

Java代码   收藏代码
  1. // 检查类是否已被装载过  
  2. Class c = findLoadedClass(name);  
  3. if (c == null ) {  
  4.      // 指定类未被装载过  
  5.      try {  
  6.          if (parent != null ) {  
  7.              // 如果父类加载器不为空, 则委派给父类加载  
  8.              c = parent.loadClass(name, false );  
  9.          } else {  
  10.              // 如果父类加载器为空, 则委派给启动类加载加载  
  11.              c = findBootstrapClass0(name);  
  12.          }  
  13.      } catch (ClassNotFoundException e) {  
  14.          // 启动类加载器或父类加载器抛出异常后, 当前类加载器将其  
  15.          // 捕获, 并通过findClass方法, 由自身加载  
  16.          c = findClass(name);  
  17.      }  
  18. }  

 

用Class.forName加载类
Class.forName使用的是被调用者的类加载器来加载类的.
这种特性, 证明了java类加载器中的名称空间是唯一的, 不会相互干扰.

即在一般情况下, 保证同一个类中所关联的其他类都是由当前类的类加载器所加载的.

 

Java代码   收藏代码
  1. public static Class forName(String className)  
  2.      throws ClassNotFoundException {  
  3.      return forName0(className, true , ClassLoader.getCallerClassLoader());  
  4. }  
  5.    
  6. /** Called after security checks have been made. */  
  7. private static native Class forName0(String name, boolean initialize,  
  8. ClassLoader loader)  
  9.      throws ClassNotFoundException;  

 

上图中 ClassLoader.getCallerClassLoader 就是得到调用当前forName方法的类的类加载器

 

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

 

Java代码   收藏代码
  1. // Now create the class loader to use to launch the application  
  2. try {  
  3.     loader = AppClassLoader.getAppClassLoader(extcl);  
  4. } catch (IOException e) {  
  5.     throw new InternalError(  
  6. "Could not create application class loader" );  
  7. }  
  8.    
  9. // Also set the context class loader for the primordial thread.  
  10. Thread.currentThread().setContextClassLoader(loader);  

 

以上代码摘自sun.misc.Launch的无参构造函数Launch()。

使用线程上下文类加载器, 可以在执行线程中, 抛弃双亲委派加载链模式, 使用线程上下文里的类加载器加载类.
典型的例子有, 通过线程上下文来加载第三方库jndi实现, 而不依赖于双亲委派.

大部分java app服务器(jboss, tomcat..)也是采用contextClassLoader来处理web服务。
还有一些采用 hotswap 特性的框架, 也使用了线程上下文类加载器, 比如 seasar (full stack framework in japenese).

线程上下文从根本解决了一般应用不能违背双亲委派模式的问题.

使java类加载体系显得更灵活.

 

随着多核时代的来临, 相信多线程开发将会越来越多地进入程序员的实际编码过程中. 因此,
在编写基础设施时, 通过使用线程上下文来加载类, 应该是一个很好的选择.

 

当然, 好东西都有利弊. 使用线程上下文加载类, 也要注意, 保证多根需要通信的线程间的类加载器应该是同一个,
防止因为不同的类加载器, 导致类型转换异常(ClassCastException).

 

自定义的类加载器实现
defineClass(String name, byte[] b, int off, int len,ProtectionDomain protectionDomain)
是java.lang.Classloader提供给开发人员, 用来自定义加载class的接口.

使用该接口, 可以动态的加载class文件.

 

例如,
在jdk中, URLClassLoader是配合findClass方法来使用defineClass, 可以从网络或硬盘上加载class.

而使用类加载接口, 并加上自己的实现逻辑, 还可以定制出更多的高级特性.

 

比如,

一个简单的hot swap 类加载器实现:

 

 

Java代码   收藏代码
  1. import java.io.File;  
  2. import java.io.FileInputStream;  
  3. import java.lang.reflect.Method;  
  4. import java.net.URL;  
  5. import java.net.URLClassLoader;  
  6.    
  7. /** 
  8. * 可以重新载入同名类的类加载器实现 
  9.   
  10. * 放弃了双亲委派的加载链模式. 
  11. * 需要外部维护重载后的类的成员变量状态. 
  12. * @author ken.wu 
  13. * @mail ken.wug@gmail.com 
  14. * 2007-9-28 下午01:37:43 
  15. */  
  16. public class HotSwapClassLoader extends URLClassLoader {  
  17.    
  18.     public HotSwapClassLoader(URL[] urls) {  
  19.         super (urls);  
  20.     }  
  21.    
  22.     public HotSwapClassLoader(URL[] urls, ClassLoader parent) {  
  23.         super (urls, parent);  
  24.     }  
  25.    
  26.     public Class load(String name)  
  27.           throws ClassNotFoundException {  
  28.         return load(name, false );  
  29.     }  
  30.    
  31.     public Class load(String name, boolean resolve)  
  32.           throws ClassNotFoundException {  
  33.         if ( null != super .findLoadedClass(name))  
  34.             return reload(name, resolve);  
  35.    
  36.         Class clazz = super .findClass(name);  
  37.    
  38.         if (resolve)  
  39.             super .resolveClass(clazz);  
  40.    
  41.         return clazz;  
  42.     }  
  43.    
  44.     public Class reload(String name, boolean resolve)  
  45.           throws ClassNotFoundException {  
  46.         return new HotSwapClassLoader( super .getURLs(), super .getParent()).load(  
  47.             name, resolve);  
  48.     }  
  49. }  
  50.    
  51. public class A {  
  52.     private B b;  
  53.    
  54.     public void setB(B b) {  
  55.          this .b = b;  
  56.     }  
  57.    
  58.     public B getB() {  
  59.          return b;  
  60.     }  
  61. }  
  62.    
  63. public class B {}  

 

 

这个类的作用是可以重新载入同名的类, 但是, 为了实现hotswap, 老的对象状态
需要通过其他方式拷贝到重载过的类生成的全新实例中来。(A类中的b实例)

而新实例所依赖的B类如果与老对象不是同一个类加载器加载的, 将会抛出类型转换异常(ClassCastException).

为了解决这种问题, HotSwapClassLoader自定义了load方法. 即当前类是由自身classLoader加载的, 而内部依赖的类

 

还是老对象的classLoader加载的.

 
Java代码   收藏代码
  1. public class TestHotSwap {  
  2. public static void main(String args[]) {  
  3.     A a = new A();  
  4.     B b = new B();  
  5.     a.setB(b);  
  6.    
  7.     System.out.printf("A classLoader is %s n" , a.getClass().getClassLoader());  
  8.     System.out.printf("B classLoader is %s n" , b.getClass().getClassLoader());  
  9.     System.out.printf("A.b classLoader is %s n" ,   a.getB().getClass().getClassLoader());  
  10.    
  11.     HotSwapClassLoader c1 = new HotSwapClassLoader( new URL[]{ new URL( "file:\e:\test\")} , a.getClass().getClassLoader());  
  12.     Class clazz = c1.load(" test.hotswap.A ");  
  13.     Object aInstance = clazz.newInstance();  
  14.    
  15.     Method method1 = clazz.getMethod(" setB ", B.class);  
  16.     method1.invoke(aInstance, b);  
  17.    
  18.     Method method2 = clazz.getMethod(" getB ", null);  
  19.     Object bInstance = method2.invoke(aInstance, null);  
  20.    
  21.     System.out.printf(" reloaded A.b classLoader is %s n", bInstance.getClass().getClassLoader());  
  22. }  
  23. }  
 

 

输出

A classLoader is sun.misc.Launcher$AppClassLoader@19821f
B classLoader is sun.misc.Launcher$AppClassLoader@19821f
A.b classLoader is sun.misc.Launcher$AppClassLoader@19821f
reloaded A.b classLoader is sun.misc.Launcher$AppClassLoader@19821f

 



已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [java classloader 体系结构] 推荐:

java之classloader体系结构

- - 非技术 - ITeye博客
原文出处: http://www.iteye.com/topic/136427. a, Bootstrap ClassLoader/启动类加载器. 主要负责jdk_home/lib目录下的核心 api 或 -Xbootclasspath 选项指定的jar包装入工作.. b, Extension ClassLoader/扩展类加载器.

Jetty8.0.4的ClassLoader的体系结构

- - 龙浩的blog
    1:JDK的ClassLoader基础. Bootstrap ClassLoader/启动类加载器 . 主要负责jdk_home/lib目录下的核心 api 或 -Xbootclasspath 选项指定的jar包装入工作. Extension ClassLoader/扩展类加载器 . 主要负责jdk_home/lib/ext目录下的jar包或 -Djava.ext.dirs 指定目录下的jar包装入工作.

Java基础—ClassLoader的理解

- - ZJD'S NOTES
} ``` 其余两个ClassLoader都是继承自`ClassLoader`这个类. Java的类加载采用了一种叫做“双亲委托”的方式(稍后解释),所以除了`Bootstarp ClassLoader`其余的ClassLoader都有一个“父”类加载器, 不是通过集成,而是一种包含的关系. ``` ##“双亲委托” 所谓“双亲委托”就是当加载一个类的时候会先委托给父类加载器去加载,当父类加载器无法加载的时候再尝试自己去加载,所以整个类的加载是“自上而下”的,如果都没有加载到则抛出`ClassNotFoundException`异常.

Java ClassLoader原理分析

- - Java - 编程语言 - ITeye博客
一、JDK默认提供的三个ClassLoader. JDK 默认提供了如下几种ClassLoader. Bootstrp加载器是用C++语言写的,它是在Java虚拟机启动后初始化的,它主要负责加载 %JAVA_HOME%/jre/lib, -Xbootclasspath参数指定的路径以及 %JAVA_HOME%/jre/classes中的类.

理解Java虚拟机体系结构

- - ImportNew
众所周知,Java支持平台无关性、安全性和网络移动性. 而Java平台由Java虚拟机和Java核心类所构成,它为纯Java程序提供了统一的编程接口,而不管下层操作系统是什么. 正是得益于Java虚拟机,它号称的“一次编译,到处运行”才能有所保障. 1.1 Java程序执行流程. Java程序的执行依赖于编译环境和运行环境.

Java类加载器classLoader的工作机制

- - CSDN博客推荐文章
类加载器就是寻找类或接口字节码文件进行解析并构造JVM内部对象表示的组件. 在Java中,类转载器把一个类装入JVM中,需要经过以下步骤:. 1.装载:查找和导入Class文件. 2.链接: 执行校验、准备和解析步骤,其中解析步骤是可以选择的:. a)校验: 检查载入Class文件数据的正确性;. b)准备:给类的静态变量分配存储空间;.

深入浅出ClassLoader(译)

- - 并发编程网 - ifeve.com
你真的了解ClassLoader吗. 这篇文章翻译自zeroturnaround.com的 . Do You Really Get Classloaders? ,融入和补充了笔者的一些实践、经验和样例. 本文的例子比原文更加具有实际意义,文字内容也更充沛一些,非常感谢作者  Jevgeni Kabanov 能够共享如此优秀的文档.

classLoader卸载与jvm热部署 - redcreen

- - 博客园_redcreen的专栏
      以下的相关介绍都是在未使用dcevm的情况. classLoader的卸载机制.       jvm中没有提供class及classloader的unload方法.那热部署及osgi中是通过什么机制来实现的呢?实现思路主要是通过更换classLoader进行重新加载.之前的classloader及加载的class类在没有实例引用的情况下,在perm区gc的情况下会被回收掉..

JVM学习 - 体系结构

- - CSDN博客推荐文章
一:Java技术体系模块图. 二:JVM运行时内存区域模型. 也称"永久代” 、“非堆”,  它用于存储虚拟机加载的类信息、常量、静态变量、是各个线程共享的内存区域. 可以通过-XX:PermSize 和 -XX:MaxPermSize 参数限制方法区的大小. 运行时常量池:是方法区的一部分,Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译器生成的各种符号引用,这部分内容将在类加载后放到方法区的运行时常量池中.

【Oracle】物理体系结构

- - CSDN博客推荐文章
一、ORACLE 物理体系结构. PGA: 私有内存区,仅供当前发起用户使用. 用户登录后的session信息会保存在PGA. 执行排序,如果内存不够,oracle会在临时表空间中完成. SGA: 包含共享池,数据缓冲区,日志缓冲区以及一些相关的进程. DATABASE: 数据最终存放的地方,其中一块区域是日志存放区.