`

Java ClassLoader介绍

    博客分类:
  • JAVA
 
阅读更多

2.1 执行过程简介

当编译和连接一个C++程序时,所获得的可执行二进制文件只能在指定的硬件平台和操作系统上运行,因为这个二进制文件包含了对目标处理器的机器语言。而Java编译器把Java源文件的指令翻译成字节码,这种字节码就是Java虚拟机的“机器语言”。
与普通程序不同的是,Java程序(class文件)并不是本地的可执行程序。当运行Java程序时,首先运行JVM(Java虚拟机),然后再把Java class加载到JVM里头运行,负责加载Java class的这部分就叫做Class Loader。

2.2 JVM中的ClassLoader

JVM本身包含了一个ClassLoader称为Bootstrap ClassLoader,和JVM一样,BootstrapClassLoader是用本地代码实现的,它负责加载核心JavaClass(即所有java.*开头的类)。
另外JVM还会提供两个ClassLoader,它们都是用Java语言编写的,由BootstrapClassLoader加载;其中Extension ClassLoader负责加载扩展的Javaclass(例如所有javax.*开头的类和存放在JRE的ext目录下的类)ApplicationClassLoader负责加载应用程序自身的类。
当运行一个程序的时候,JVM启动,运行bootstrapclassloader,该ClassLoader加载java核心API(ExtClassLoader和AppClassLoader也在此时被加载),然后调用ExtClassLoader加载扩展API,最后AppClassLoader加载CLASSPATH目录下定义的Class,这就是一个程序最基本的加载流程。

2.3 执行ClassLoader例子

什么时候JVM会使用ClassLoader加载一个类呢?当你使用java去执行一个类,JVM使用ApplicationClassLoader加载这个类;然后如果类A引用了类B,不管是直接引用还是用Class.forName()引用,JVM就会找到加载类A的ClassLoader,并用这个ClassLoader来加载类B。JVM按照运行时的有效执行语句,来决定是否需要装载新类,从而装载尽可能少的类,这一点和编译类是不相同的。

 

 

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. * 需要外部维护重载后的类的成员变量状态. 
  13. * 
  14. * @author ken.wu 
  15. * @mail ken.wug@gmail.com 
  16. * 2007-9-28 下午01:37:43 
  17. */  
  18. public class HotSwapClassLoader extends URLClassLoader {  
  19.    
  20.     public HotSwapClassLoader(URL[] urls) {  
  21.         super (urls);  
  22.     }  
  23.    
  24.     public HotSwapClassLoader(URL[] urls, ClassLoader parent) {  
  25.         super (urls, parent);  
  26.     }  
  27.    
  28.     public Class load(String name)  
  29.           throws ClassNotFoundException {  
  30.         return load(name, false );  
  31.     }  
  32.    
  33.     public Class load(String name, boolean resolve)  
  34.           throws ClassNotFoundException {  
  35.         if ( null != super .findLoadedClass(name))  
  36.             return reload(name, resolve);  
  37.    
  38.         Class clazz = super .findClass(name);  
  39.    
  40.         if (resolve)  
  41.             super .resolveClass(clazz);  
  42.    
  43.         return clazz;  
  44.     }  
  45.    
  46.     public Class reload(String name, boolean resolve)  
  47.           throws ClassNotFoundException {  
  48.         return new HotSwapClassLoader( super .getURLs(), super .getParent()).load(  
  49.             name, resolve);  
  50.     }  
  51. }  
  52.    
  53. public class A {  
  54.     private B b;  
  55.    
  56.     public void setB(B b) {  
  57.          this .b = b;  
  58.     }  
  59.    
  60.     public B getB() {  
  61.          return b;  
  62.     }  
  63. }  
  64.    
  65. 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

 
 
分享到:
评论

相关推荐

    Java ClassLoader定制实例

    在Java编程语言中,ClassLoader是一个至关重要的组成部分,它负责加载类到JVM(Java虚拟机)中。理解ClassLoader的工作原理以及如何定制它,对于深入学习Java的运行机制和进行高级应用开发具有重要意义。本篇文章将...

    java classloader

    Java ClassLoader是一个核心的Java运行时组件,负责加载类到Java虚拟机(JVM)中。它是Java平台的独特特性,因为它允许动态加载类,增强了软件的可扩展性和灵活性。这篇博文(虽然链接不可用)可能深入探讨了...

    java classloader classpath 张孝祥

    ### Java ClassLoader与ClassPath详解 #### 一、概述 在Java编程中,类加载机制是十分关键的一个环节。类加载器(`ClassLoader`)负责将编译后的`.class`文件加载到Java虚拟机(JVM)中执行,而类路径(`ClassPath...

    破解java加密的ClassLoader.java,在classloader植入破解代码

    破解java加密的ClassLoader.java,在classloader植入破解代码

    Java ClassLoader原理

    ### Java ClassLoader原理详解 #### 摘要 本文探讨了Java虚拟机(JVM)中的一个重要特性:动态类加载(Dynamic Class Loading)。这一机制为Java平台提供了强大的能力,允许在运行时安装软件组件,例如从网络下载...

    java ClassLoader机制及其在OSGi中的应用

    Java ClassLoader机制是Java虚拟机(JVM)中一个至关重要的组成部分,它的主要任务是将类的.class文件加载到JVM中,使得程序能够运行。ClassLoader不仅负责类的加载,还涉及类的验证、初始化等一系列过程。理解...

    理解Java ClassLoader机制

    Java ClassLoader机制是Java运行时环境中的核心组件之一,它负责加载类到JVM(Java虚拟机)中,使得程序能够执行。理解ClassLoader的工作原理对于优化应用性能、处理类加载问题以及实现自定义加载器至关重要。 首先...

    Java ClassLoader学习总结

    Java ClassLoader学习总结 Java 类加载机制是 Java 中一个非常重要的机制,它负责加载 Class 文件到 JVM,以供程序使用。ClassLoader 是 Java 中的一个抽象类,它的主要作用是加载 Class 文件到 JVM 中。...

    Understanding the Java ClassLoader

    为了更好地理解和利用Java的这一特性,本篇将详细介绍Java ClassLoader的作用及其工作原理,并通过构建一个示例ClassLoader来帮助读者深入理解如何自定义ClassLoader,从而扩展JVM的功能。 #### 二、ClassLoader...

    Java ClassLoader Tutorial.zip

    Java 类加载器(ClassLoader)是Java虚拟机(JVM)的重要组成部分,它负责在运行时查找并加载类到JVM中。这个教程将深入探讨ClassLoader的工作原理、类型以及如何自定义类加载器。 一、Java ClassLoader 基础 1. 类...

    java classloader讲义-淘宝网

    ### Java ClassLoader详解:以淘宝网为例...以上内容详细介绍了Java ClassLoader的基本概念、工作原理、自定义方式及其实战应用,并结合淘宝网的实际案例进行了分析,希望能够帮助读者更深入地理解和运用ClassLoaders。

    JAVA ClassLoader 讲解 (类加载器)

    ### Java ClassLoader (类加载器)详解 #### 一、教程提示 如果你正在查看这份文档,在线版中你可以点击下面的任何主题直接跳转到相应的部分。 1. **教程提示** 2. **介绍** 3. **类加载器结构** 4. **编译类加载...

    java classLoader 的全面讲解

    Java 类加载器(ClassLoader)是Java虚拟机(JVM)中的一个重要组成部分,它负责加载类的字节码文件,使得程序能够运行。深入理解ClassLoader对于优化应用性能、处理类加载问题以及实现自定义加载策略至关重要。 一...

    了解Java ClassLoader

    下面将详细介绍Java ClassLoader的基本概念、工作流程、类加载机制以及自定义ClassLoader。 1. **ClassLoader基本概念** - ClassLoader是一个Java类,用于动态加载Java类到JVM中。Java程序中的每个类都由某个...

    java中classLoader的使用

    Java中的类加载器(ClassLoader)是Java虚拟机(JVM)的一个重要组成部分,它负责将类的.class文件从文件系统或者网络中加载到内存中,并转换为对应的Class对象。类加载器的工作流程主要包括加载、验证、准备、解析...

    Understanding the Java ClassLoader.pdf

    在深入探讨Java类加载器(ClassLoader)之前,我们首先需要了解它在Java运行时系统中的核心作用。类加载器是Java虚拟机(JVM)的一个关键组成部分,负责查找、加载和链接类文件到JVM中。它的存在使得Java应用程序...

    classloader-playground, 一个简单的java依赖隔离容器类.zip

    《深入理解Java类加载器:基于classloader-playground开源项目》 在Java世界里,类加载器(ClassLoader)是理解JVM工作原理的关键一环。它负责将字节码文件(.class)从磁盘、网络或其他数据源加载到内存,并转化为...

Global site tag (gtag.js) - Google Analytics