【JVM】类加载器与双亲委派
类加载器,顾名思义,即是实现类加载的功能模块,负责将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;
}
}
综上,类加载器加载类的具体流程如下:
- 先检查本类加载器是否加载类,如果已加载,结束,没加载进入下一步;
- 递归委派上级类加载器;
- 如果没有上级了,执行启动类加载器,查找类是否存在对应的路径下;
- 上级都没有找到类时,跳出递归,在本类加载器对应路径下查找类。
双亲委派的好处:根据双亲委派模型的特点,可以知道,越是基础的类,由越上层的类加载器来加载,如此一来,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】类加载器与双亲委派的更多相关文章
- JVM类加载器以及双亲委派模型
从java开发人员的角度来看,类加载器可以分为3种: 1.启动类加载器(Bootstrap ClassLoader),负责将存放在<JAVA_HOME>\lib目录中,或者被-Xbootc ...
- jvm类加载器以及双亲委派
首先来了解几个概念: 类加载: 概念:虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验--转换解析--初始化,最终形成能被java虚拟机直接使用的java类型,就是jvm的类加载机制. ...
- [jvm] -- 类加载器及双亲委派模板篇
类加载器 JVM 中内置了三个重要的 ClassLoader BootstrapClassLoader(启动类加载器):最顶层的加载类,由C++实现,负责加载 %JAVA_HOME%/lib目录下的j ...
- JVM——类加载器的双亲委派模型
类加载器双亲委派模型,如下图所示: 双亲委派模型的工作过程 如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此 ...
- jvm类加载器和双亲委派模型
类加载器按照层次,从顶层到底层,分为以下三种: (1)启动类加载器(Bootstrap ClassLoader) 这个类加载器负责将存放在JAVA_HOME/lib下的,或者被-Xbootcla ...
- 【深入理解JVM】类加载器与双亲委派模型
原文链接:http://blog.csdn.net/u011080472/article/details/51332866,http://www.cnblogs.com/lanxuezaipiao/p ...
- JVM类加载机制详解(二)类加载器与双亲委派模型
在上一篇JVM类加载机制详解(一)JVM类加载过程中说到,类加载机制的第一个阶段加载做的工作有: 1.通过一个类的全限定名(包名与类名)来获取定义此类的二进制字节流(Class文件).而获取的方式,可 ...
- 【深入理解JVM】:类加载器与双亲委派模型
类加载器 加载类的开放性 类加载器(ClassLoader)是Java语言的一项创新,也是Java流行的一个重要原因.在类加载的第一阶段“加载”过程中,需要通过一个类的全限定名来获取定义此类的二进制字 ...
- 【深入理解JVM】类加载器与双亲委派模型 (转)
出处: [深入理解JVM]类加载器与双亲委派模型 加载类的开放性 类加载器(ClassLoader)是Java语言的一项创新,也是Java流行的一个重要原因.在类加载的第一阶段“加载”过程中,需要通过 ...
- 类文件的结构、JVM 的类加载过程、类加载机制、类加载器、双亲委派模型
一.类文件的结构 我们都知道,各种不同平台的虚拟机,都支持 "字节码 Byte Code" 这种程序存储格式,这构成了 Java 平台无关性的基石.甚至现在平台无关性也开始演变出 ...
随机推荐
- TP5 RCE
Thinkphp5 RCE 复现 环境: win10+wamp+thinkphp5.1.29 下载地址 源码分析 程序首先跳转到 public目录下的index.php,然后执行 thinkphp/l ...
- tcp 输入 简析 转载
正常来说 TCP 收消息过程会涉及三个队列: Backlog Queue sk->sk_backlog Prequeue tp->ucopy.prequeue Receive Queue ...
- maven pom.xml 报错
首先介绍背景,在eclipse中导入一个maven的项目,在我之前的电脑上导入好用,在自己的电脑上导入居然pom报错了Missing artifact junit:junit:jar:4.11,还会有 ...
- mysql数据库安装与卸载以及Navicat安装
关系型数据库Mysql 1.mysql官网网址:www.mysql.org 2.mysql数据库安装 傻瓜式安装下载地址 https://dev.mysql.com/downloads/install ...
- Java解释单链表中的头插法以及尾插法
单链表属于数据结构中的一种基本结构,是一种线性结构,在此使用Java对其中的头插法以及尾插法进行解释. 首先定义好链表中的节点类: 其中,data代表节点所存放的数据,next代表指向下一节点 对于单 ...
- IDEA “Cannot resolve symbol” 解决办法
IDEA 无法识别同一个 package 里的其他类,将其显示为红色,但是 compile 没有问题.鼠标放上去后显示 "Cannot resolve symbol XXX",重启 ...
- phpmyadmin 4.8.1任意文件包含(CVE-2018-12613)
简介 环境复现:https://gitee.com/xiaohua1998/hctf_2018_warmup 考察知识点:文件包含漏洞(phpmyadmin 4.8.1任意文件包含) 线上平台:榆林学 ...
- c#多播委托判断空值
int resualt = (int)fn?.Invoke(a, b); 其中fn是有两个参数一个int返回值的多播委托,这个看起来很精炼,大概意思也不懂 个人理解为使用先判断fn?,再传值以及输出为 ...
- 转化dataframe中一组序列为时间序列的方法-to_datetime()的最新用法
一.to_datetime()的最新用法: hs300_hf['date'] = pd.to_datetime(hs300_hf['date']) hs300_hf.set_index('date', ...
- FL Studio中有关Sub Bass的一些制作与混音技巧
1.Sub Bass是什么? Sub Bass是一种低沉的低音,其频率大约低于60赫兹,并向下延伸,包括人类所能听到的最低频率,约为20赫兹.在这个范围内,人类的听觉不是很灵敏,所以在这个范围内的声音 ...