类加载器,顾名思义,即是实现类加载的功能模块,负责将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来自定义类加载器。

protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
Class<?> c = findLoadedClass(name);//检查名称为name的类是否被本类加载器加载过
if (c == null) {// 为null表示没有被加载
long t0 = System.nanoTime();
try {
if (parent != null) {//上级不为null
c = parent.loadClass(name, false);//递归调用上级类加载器loadClass方法
} else {//上级为null,即表示上级是启动类加载器
c = findBootstrapClassOrNull(name);//委托启动类加载器在javaHome/jre/lib下寻找名称为name的类
}
} catch (ClassNotFoundException e) {
// 如果上级类加载器没有找到名称为name的类,则在此处捕获ClassNotFoundException异常
} if (c == null) {//c为null表示上级类加载器没有找到name类
long t1 = System.nanoTime();
c = findClass(name);//到本加载器的路径下寻找名称为name的类(例如扩展类加载器则是lib/ext/下) // this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);// 进行类的连接操作
}
return c;
}
}

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

  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,如下所示

class Thread {
...
private ClassLoader contextClassLoader; public ClassLoader getContextClassLoader() {
return contextClassLoader;
} public void setContextClassLoader(ClassLoader cl) {
this.contextClassLoader = cl;
}
...
}

这个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. 【JVM第七篇】执行引擎

    写在前面的话:本文是在观看尚硅谷JVM教程后,整理的学习笔记.其观看地址如下:尚硅谷2020最新版宋红康JVM教程 执行引擎是Java虚拟机中的核心组成部分. 执行引擎的作用就是解析虚拟机字节码指令, ...

  2. Selective Acknowledgment 选项 浅析 2

    来自:http://abcdxyzk.github.io/blog/2013/09/06/kernel-net-sack/ static int tcp_sacktag_write_queue(str ...

  3. tcpack---1简述

    TCP重传机制 TCP要保证所有的数据包都可以到达,所以,必需要有重传机制. 超时重传机制 一种是不回ack,死等3,当发送方发现收不到3的ack超时后,会重传3.一旦接收方收到3后,会ack 回 4 ...

  4. 11Linux之软件包管理

    11Linux之软件包管理 目录 11Linux之软件包管理 11 软件包管理 11.1 软件包介绍 11.1.1 编程语言分类 11.1.2 三种安装包 11.2 rpm包管理 11.2.1 rpm ...

  5. ubuntu服务器dns重启失效问题

    方法一 通过/etc/network/interfaces,在它的最后增加一句: dns-nameservers 8.8.8.8 8.8.8.8是Google提供的DNS服务,这里只是举一个例子,你也 ...

  6. hive简单的项目实战

    解压user.zip [root@hadoop1 test]# unzip user.zip -d /test/bigdatacase/dataset Archive: user.zip inflat ...

  7. 查看 /var/log目录下文件个数 命令tree 、cut

    查看 /var/log目录下文件个数 方法1. [root@oldboy learn_shell]# tree -L 1 /var/log/ |tail -1 5 directories, 42 fi ...

  8. Python 调用接口添加头信息

    import requests,jsonurl = 'http://47.108.115.193:9000/tb-store/store/getWechatAppHome'header={" ...

  9. DNS域传输漏洞复现

    漏洞原理 DNS分类 常见的DNS记录类型 A IP地址记录,记录一个域名对应的IP地址 AAAA IPv6 地址记录,记录一个域名对应的IPv6地址 CNAME 别名记录,记录一个主机的别名 MX ...

  10. Vegas视频FX功能详解

    今天呢,小编就带大家走进Vegas(Win系统)视频FX的世界.那么什么是视频FX呢,就是视频制作软件Vegas中自带添加特效的地方,它可以用于添加模糊,黑白,镜像等滤镜效果,各种高大上的视频大片都需 ...