类加载器,顾名思义,即是实现类加载的功能模块,负责将Class的字节码形式加载成内存形式的Class对象。字节码文件可来源于磁盘或者jar包中的Class文件,也可以来自网络字节流。

类加载器

在JVM中,内置了三个重要的类加载器,Application classLoader,Extension classLoader和Bootstrap classLoader。应用类加载器,扩展类加载器和启动类加载器。

  • Bootstrap classLoader启动类加载器:加载JAVA_HOME/lib/rt.jar下的核心类,比如 java.util.*、java.io.*、java.nio.*、java.lang.* 等等。使用C代码实现,Java无法访问。
  • Extension classLoader扩展类加载器:加载JAVA_HOME/lib/ext/*.jar中的扩展类,比如 swing 系列、内置的 js 引擎、xml 解析器 等等,这些库名通常以 javax 开头。
  • Application classLoader应用类加载器:加载Classpath环境变量里定义的路径中的jar包和目录。自己编写的代码和第三方jar都由该类加载器加载。

三种类加载器存在传递性。Application classLoader 加载类时,会先问问Extension classLoader是否加载过,会在再问问Bootstrap classLoader是否加载过。

每个Class对象里面都有一个classLoader属性记录当前类由哪个类加载器加载

双亲委派模型

双亲委派机制也很好理解,AppClassLoader只负责加载ClassPath下的class文件,需要加载系统类库时,会委托上级类加载器,BootstrapClassLoader和ExtensionClassLoader,去加载对应的类库,这就是所谓的“双亲委派模型”

下面通过源码来分析双亲委派的流程。此处的loadClass方法来源于类加载器抽象类ClassLoader。该方法是加载类的入口。用户可以继承ClassLoader来自定义类加载器。

  1. protected Class<?> loadClass(String name, boolean resolve)
  2. throws ClassNotFoundException
  3. {
  4. synchronized (getClassLoadingLock(name)) {
  5. Class<?> c = findLoadedClass(name);//检查名称为name的类是否被本类加载器加载过
  6. if (c == null) {// 为null表示没有被加载
  7. long t0 = System.nanoTime();
  8. try {
  9. if (parent != null) {//上级不为null
  10. c = parent.loadClass(name, false);//递归调用上级类加载器loadClass方法
  11. } else {//上级为null,即表示上级是启动类加载器
  12. c = findBootstrapClassOrNull(name);//委托启动类加载器在javaHome/jre/lib下寻找名称为name的类
  13. }
  14. } catch (ClassNotFoundException e) {
  15. // 如果上级类加载器没有找到名称为name的类,则在此处捕获ClassNotFoundException异常
  16. }
  17. if (c == null) {//c为null表示上级类加载器没有找到name类
  18. long t1 = System.nanoTime();
  19. c = findClass(name);//到本加载器的路径下寻找名称为name的类(例如扩展类加载器则是lib/ext/下)
  20. // this is the defining class loader; record the stats
  21. sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
  22. sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
  23. sun.misc.PerfCounter.getFindClasses().increment();
  24. }
  25. }
  26. if (resolve) {
  27. resolveClass(c);// 进行类的连接操作
  28. }
  29. return c;
  30. }
  31. }

综上,类加载器加载类的具体流程如下:

  1. 先检查本类加载器是否加载类,如果已加载,结束,没加载进入下一步;
  2. 递归委派上级类加载器;
  3. 如果没有上级了,执行启动类加载器,查找类是否存在对应的路径下;
  4. 上级都没有找到类时,跳出递归,在本类加载器对应路径下查找类。

双亲委派的好处:根据双亲委派模型的特点,可以知道,越是基础的类,由越上层的类加载器来加载,如此一来,Java类随着类加载具备了一个带有优先级地层次关系。这样可以保证,若用户编写了与Java类库中的类重名的类,此类不会被加载,因为同名的类往往是会被委派给启动类加载器或扩展类加载器来加载。因此双亲委派机制可以保证Java程序的稳定性

破坏双亲委派

在双亲委派机制中,我们知道,基础的类由上级类加载器加载。双亲委派可以保证“基础类作为API被用户代码调用”这个场景能够准确运行。但是,可能会存在一些基础类调用用户代码的情况。例如,Java提供了很多服务提供者接口(Service Provider Interface, SPI),允许第三方为这些接口提供实现,常见的SPI实现有JDBC、JCE、JNDI、JAXP等。SPI的接口由Java核心库提供,而其实现代码则是属于应用程序的jar包(放进CLASSPATH中)。那么问题来了,核心库中的SPI的接口由启动类加载来加载,CLASSPATH中的实现类由应用类加载器来加载,此种应用场景下,启动类加载器是无法找到SPI的实现类的。

因此,需要通过某种特殊手段,来打破双亲委派,让上级类加载器找不到类时,调用能获取到目标类的下级类加载器来进行加载。jdk引入了“线程上下文类加载器”来解决此问题。

线程上下文类加载器

在Thread类中有一个成员变量,contextClassLoader,如下所示

  1. class Thread {
  2. ...
  3. private ClassLoader contextClassLoader;
  4. public ClassLoader getContextClassLoader() {
  5. return contextClassLoader;
  6. }
  7. public void setContextClassLoader(ClassLoader cl) {
  8. this.contextClassLoader = cl;
  9. }
  10. ...
  11. }

这个contextClassLoader就是线程上下文类加载器,用于引用一个类加载器。可以通过setContextClassLoader方法进行设置,若不设置,线程会从父线程中继承一个类加载器。main线程的contextClassLoader是应用类加载器,因此默认情况contextClassLoader都指向AppClassLoader

按照双亲委派的机制,上级的BootStrapClassLoader无法委派下级AppClassLoader来加载类,但是可以通过线程中的contextClassLoader来获取到AppClassLoader进行类加载。如此一来,便可以打破双亲委派的层次结构来逆向使用类加载器。

【JVM】类加载器与双亲委派的更多相关文章

  1. JVM类加载器以及双亲委派模型

    从java开发人员的角度来看,类加载器可以分为3种: 1.启动类加载器(Bootstrap ClassLoader),负责将存放在<JAVA_HOME>\lib目录中,或者被-Xbootc ...

  2. jvm类加载器以及双亲委派

    首先来了解几个概念: 类加载: 概念:虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验--转换解析--初始化,最终形成能被java虚拟机直接使用的java类型,就是jvm的类加载机制. ...

  3. [jvm] -- 类加载器及双亲委派模板篇

    类加载器 JVM 中内置了三个重要的 ClassLoader BootstrapClassLoader(启动类加载器):最顶层的加载类,由C++实现,负责加载 %JAVA_HOME%/lib目录下的j ...

  4. JVM——类加载器的双亲委派模型

    类加载器双亲委派模型,如下图所示: 双亲委派模型的工作过程 如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此 ...

  5. jvm类加载器和双亲委派模型

    类加载器按照层次,从顶层到底层,分为以下三种:  (1)启动类加载器(Bootstrap ClassLoader)   这个类加载器负责将存放在JAVA_HOME/lib下的,或者被-Xbootcla ...

  6. 【深入理解JVM】类加载器与双亲委派模型

    原文链接:http://blog.csdn.net/u011080472/article/details/51332866,http://www.cnblogs.com/lanxuezaipiao/p ...

  7. JVM类加载机制详解(二)类加载器与双亲委派模型

    在上一篇JVM类加载机制详解(一)JVM类加载过程中说到,类加载机制的第一个阶段加载做的工作有: 1.通过一个类的全限定名(包名与类名)来获取定义此类的二进制字节流(Class文件).而获取的方式,可 ...

  8. 【深入理解JVM】:类加载器与双亲委派模型

    类加载器 加载类的开放性 类加载器(ClassLoader)是Java语言的一项创新,也是Java流行的一个重要原因.在类加载的第一阶段“加载”过程中,需要通过一个类的全限定名来获取定义此类的二进制字 ...

  9. 【深入理解JVM】类加载器与双亲委派模型 (转)

    出处: [深入理解JVM]类加载器与双亲委派模型 加载类的开放性 类加载器(ClassLoader)是Java语言的一项创新,也是Java流行的一个重要原因.在类加载的第一阶段“加载”过程中,需要通过 ...

  10. 类文件的结构、JVM 的类加载过程、类加载机制、类加载器、双亲委派模型

    一.类文件的结构 我们都知道,各种不同平台的虚拟机,都支持 "字节码 Byte Code" 这种程序存储格式,这构成了 Java 平台无关性的基石.甚至现在平台无关性也开始演变出 ...

随机推荐

  1. Linux上传递文件到另外一个Linux服务器

    现在的项目由于安全的需要,测试服务器被设置不能直接连接,想要连接的话,只能先登录一个服务器,然后以这个服务器为跳板,去登录另外一台真正的 服务器,即使是测试环境也只能这样操作.只能是相对来说安全一些. ...

  2. linux中/etc/passwd和/etc/shadow文件说明

    /etc/passwd是用来存储登陆用户信息: [root@localhost test]# cat /etc/passwd root:x:0:0:root:/root:/bin/bash bin:x ...

  3. Spring之事务源码

    对@Transactional注解的类进行动态代理 同前文<Spring AOP源码分析>中分析动态代理入口一样,都是在initializeBean时执行. Object exposedO ...

  4. 测试_QTP使用实例

    1. QTP简介 1.1QTP功能与特点 QTP是QuickTest Professional的简称,是一种自动化软件测试工具.在软件的测试过程中,QTP主要来用来通过已有的测试脚本执行重复的手动测试 ...

  5. No matching distribution found for Tensorflow

    No matching distribution found for Tensorflow 原因:python 3.7.3 版本过高 解决:安装3.6  64位

  6. Docker学习第四天(Dockerfile)

    dockerfile Dockerfile是用来构建Docker镜像的构建文件,是由一系列命令和参数构成的脚本 Dockerfile是由一系列命令和参数构成的脚本,这些命令应用于基础镜像并最终创建一个 ...

  7. bootstrap-datetimepicker的两种版本

    1.引入js/css <link rel="stylesheet" th:href="@{/plugin/bootstrap-datetimepicker/boot ...

  8. mybatis使用细节注意

    mybatis细节 增删改查ParameterType都可以不用写 解释这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数 mybatis 中 ...

  9. 如何使用Camtasia进行电脑录制屏幕

    在现在的网络互联网时代,越来越多的人走上了自媒体的道路.有些自媒体人会自己在网络上录制精彩视频,也有一些人会将精彩.热门的电影剪辑出来再加上自己给它的配音,做成大家喜欢看的电影剪辑片段.相信不管大家是 ...

  10. 【Python】python 入门与进阶

    github地址:https://github.com/wangxiao9/basic_python.git