类加载器,顾名思义,即是实现类加载的功能模块,负责将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. fork系统调用关于如何让子进程先运行{wait(),waitpid(),sleep()}

    在父进程里面调用wait()和waitpid()可以确保子进程先运行,因为当子进程运行完后会变成僵尸进程,此时会发送一个信号给父进程,父进程接受到信号才会运行. 有人或许会问如果在父进程调用wait之 ...

  2. MySQL全面瓦解11:子查询和组合查询

    概述 子查询是SQL查询中的重要一块,是我们基于多表之间进行数据聚合和判断的一种手段,使得我们的处理复杂数据更加的便捷,这一节我们主要来了解一下子查询. 先做一下数据准备,这边建立三张表:班级.学生. ...

  3. 1. 线性DP 120. 三角形最小路径和

    经典问题: 120. 三角形最小路径和  https://leetcode-cn.com/problems/triangle/ func minimumTotal(triangle [][]int) ...

  4. 通过ceph-deploy安装不同版本ceph

    之前有在论坛写了怎么用 yum 安装 ceph,但是看到ceph社区的群里还是有人经常用 ceph-deploy 进行安装,然后会出现各种不可控的情况,虽然不建议用ceph-deploy安装,但是既然 ...

  5. openstack常用命令-nova篇

    1.查看openstack版本 nova-manage version 2.查看节点 nova host-list 3.查看计算节点 nova hypervisor-list 4.查看计算节点上有哪些 ...

  6. (msf使用)msfconsole - meterpreter

    [msf] msfconsole meterpreter 对于这款强大渗透测试框架,详情介绍可看这里:metasploit 使用教程 对于msfconsole, Kali Linux 自带.只需用命令 ...

  7. 深度解析:java必须掌握的知识点——类的重用

    类继承的概念和语法 类继承的概念 根据已有类来定义新类,新类拥有已有类的所有功能. Java只支持类的单继承,每个子类只能有一一个直接超类(父类). 超类是所有子类的公共属性及方法的集合,子类则是超类 ...

  8. MathType中如何输入正、余弦函数

    MathType是一款强大的数学公式编辑器,正.余弦函数也是中学中非常重要的一节知识点,今天我们介绍一下在MathType中怎么输入正.余弦函数. 具体步骤如下: 步骤一 打开专业的公式编辑软件Mat ...

  9. python+selenium利用cookie记住密码

    先上代码 1 from selenium import webdriver 2 from time import sleep 3 4 dr = webdriver.Chrome() 5 dr.get( ...

  10. 《高并发下的.NET》第2季 - 《memcached连接暴增案》第1集:问题表现

    在<.NET 5.0 背锅案>第7集-大结局之后,园子和 .NET 继续过上了幸福生活...剧情很美好,现实很残酷...现实是旧案刚结,新案立至,而且新案与旧案有关联,被迫继续拍剧,并对该 ...