JVM----双亲委派模型
加载类的开放性
我们在了解双亲委派模型之前,不得不先了解一下什么是类加载器。虚拟机设计团队之初是希望类加载过程“通过一个类的全限定名来获取描述该类的二进制字节流”这个动作能放到虚拟机外部实现,以便于让程序自己决定如何获取该类,实现这个动作的代码的工具成为类加载器。
可能很多人觉得类加载器,顾名思义,就是个加载类的嘛,有啥大不了的,但是类加载这个过程是很严格的,对于任意一个类,我们都需要由加载他的类加载器和类的本身来决定该类在虚拟机之中的唯一性。什么意思呢??就是说我们的虚拟机要比较两个类是否相等,那前提条件是就是这两个类必须是在同一个类加载器加载的,如果两个类都不是由同一个加载器加载的,那么这俩类就一定不相等,所以就没有比较的意义!
public class ClassLoaderTest {
public static void main(String[] args) throws Exception {
ClassLoader myLoader = new ClassLoader() {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
try {
String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
InputStream is = getClass().getResourceAsStream(fileName);
if (is == null) {
return super.loadClass(name);
}
byte[] b = new byte[is.available()];
is.read(b);
return defineClass(name, b, 0, b.length);
} catch (IOException e) {
throw new ClassNotFoundException(name);
}
}
};
Object obj = myLoader.loadClass("org.fenixsoft.classloading.ClassLoaderTest").newInstance();
System.out.println(obj.getClass());
System.out.println(obj instanceof org.fenixsoft.classloading.ClassLoaderTest);
}
}
就比如上面这段代码,代码运行结果很令人失望,虽然打印出的类路径是相同的,但是比较后的结果却是false,这是为啥啊?好气啊,明明类路径一样,但是结果却不同?
这是因为,我们自己实现了属于我们自己的类加载器,我们选择了我们自己的加载路径去加载该类,而另一个同类路径名的类却是由另一个加载器(应用程序类加载器)加载的,只要不是同一个类加载器加载的类,一定不是同一个类!!
双亲委派模型
从java虚拟机角度来讲,只存在两种不同的类加载器:
(1)一种是启动类加载器,由C++语言实现的,属于虚拟机的一部分;
(2)一种是所有的其他类加载器,这些都是由Java实现的,独立于虚拟机外部,继承自java.lang.ClassLoader;
但是从开发人员角度来讲,应该分的再细一些,绝大部分程序都使用到了以下三种系统提供的类加载器:
(1)启动类加载器,该加载器是C++实现的,它负责加载存放于<JAVA_HOME>\lib目录下的类,它是仅仅按照文件的名字来识别的,名字不符合的类就算放到该目录下,也是毫无卵用的.....
(2)扩展类加载器,它是负责加载<JAVA_HOME>\lib\Ext目录下的;
(3)应用程序类加载器,这个类也被称为系统类加载器,它是负责用户类路径classpath上指定的类库,开发者可以直接使用这个加载器;
应用程序都是由这三种加载器相互配合进行加载的,有必要的话,还可以实现属于自己的类加载器,这几种加载器关系如图:

这种层次结构我们就称之为双亲委派模型,可以很直观的看出除了顶层的启动类加载器外,其他的都有属于自己的父类加载器。但是我们在这里不要混淆一个概念,就是继承(Inheritance),这个结构图并不是继承关系而是通过组合的方式来实现向上委托的.......
双亲委派的工作流程就是:如果一个类加载器收到了类加载的请求,它是不会自己立马亲自动手去加载的(比较懒,哈哈!),而是把该请求委托给父类,每一层都是如此,到了顶层后,这时就无法再向上传递了,所有的请求都集中到了启动类加载器,当父类反馈自己无法满足这个请求时,这时就会再把请求一层层向下传递。
这样的好处是啥??相信大家看这种层次结构应该很清晰,但是这有什么意义吗?比如java.lang.Object,他是存在rt.jar里的,不论哪种加载器,是系统自带的也好还是我们自己实现的也好,都会把请求一层层的往上委托,直到启动类加载器,而启动类加载器一看,自己是有这个类的,所以加载,因此Object在程序的各个类加载器的加载下永远都是同一个类。反之,没有双亲委派模型,任由各个类加载器自己去加载的话,比如我们开发者自己写了Object类,包名也是java.lang,那么系统中就会出现各种各样的Object,每一个层级的类加载器都加载了自己具有个性的Object,那么作为程序中这么基础这么重要的Object,他的唯一性得不到保证,应用程序就会杂乱不堪。
双亲委派模型的作用想必到这里很多人应该清楚了,觉得:“哇!这个模型还真的是很强大呢...”。他的实现也是非常简单的:
protected synchronized Class<?> loadClass(String name,boolean resolve)throws ClassNotFoundException{
//check the class has been loaded or not
Class c = findLoadedClass(name);
if(c == null){
try{
if(parent != null){
c = parent.loadClass(name,false);
}else{
c = findBootstrapClassOrNull(name);
}
}catch(ClassNotFoundException e){
//if throws the exception ,the father can not complete the load
}
if(c == null){
c = findClass(name);
}
}
if(resolve){
resolveClass(c);
}
return c;
}
从图中的代码,我们大致可以看出这个委托机制是如何实现的,当一个加载器收到请求后,首先会判断一下当前这个类是否已经被加载过,如果没有被加载的话,开始委托父类加载器了(就是这么懒,哈哈),如果没有父类的话,就默认使用启动类加载器。如果抛异常了,就代表当前类加载器的父类无法加载,满足不了请求,那么此时只能自己亲自出马了!!所以什么事还是自己来做的靠谱啊哈哈。
总结
当然,这种模型一直都不是强制性的,而是推荐我们这么做的,往年就出现过打破该机制的事件,典型的例子就是JNDI服务,他的代码是交给启动类加载器去实现的,但是当JNDI要对资源进行集中化管理时,他需要调用其他公司实现并部署在应用程序的classpath下的JNDI接口,因为这些代码是需要我们开发者自己来实现的,这时启动类加载器是无法识别这些类的,于是乎出现了一种线程上下文加载器,JNDI服务可以调用该加载器去加载所需要的代码,就是通过父类加载器去请求子类加载器来实现的,这已经很明显的违背了双亲委派模型。
JVM----双亲委派模型的更多相关文章
- JVM双亲委派模型及其优点
JVM双亲委派模型及其优点 什么是双亲委派模型? 双亲委派模型: 如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器, ...
- jvm双亲委派模型
其实,双亲委派模型并不复杂.自定义类加载器也不难!随便从网上搜一下就能搜出一大把结果,然后copy一下就能用.但是,如果每次想自定义类加载器就必须搜一遍别人的文章,然后复制,这样显然不行.可是自定义类 ...
- 【深入理解JVM】类加载器与双亲委派模型
原文链接:http://blog.csdn.net/u011080472/article/details/51332866,http://www.cnblogs.com/lanxuezaipiao/p ...
- JVM总括四-类加载过程、双亲委派模型、对象实例化过程
JVM总括四-类加载过程.双亲委派模型.对象实例化过程 目录:JVM总括:目录 一. 类加载过程 类加载过程就是将.class文件转化为Class对象,类实例化的过程,(User user = new ...
- JVM类加载机制详解(二)类加载器与双亲委派模型
在上一篇JVM类加载机制详解(一)JVM类加载过程中说到,类加载机制的第一个阶段加载做的工作有: 1.通过一个类的全限定名(包名与类名)来获取定义此类的二进制字节流(Class文件).而获取的方式,可 ...
- 【深入理解JVM】:类加载器与双亲委派模型
类加载器 加载类的开放性 类加载器(ClassLoader)是Java语言的一项创新,也是Java流行的一个重要原因.在类加载的第一阶段“加载”过程中,需要通过一个类的全限定名来获取定义此类的二进制字 ...
- JVM类加载过程与双亲委派模型
类加载过程 类加载过程为JVM将类描述数据从.class文件中加载到内存,并对数据进行解析和初始化,最终形成被JVM直接使用的Java类型.包含: 加载:获取该类的二进制字节流,将字节流代表的静态存储 ...
- JVM的类加载过程以及双亲委派模型详解
JVM的类加载过程以及双亲委派模型详解 这篇文章主要介绍了JVM的类加载过程以及双亲委派模型详解,类加载器就是根据指定全限定名称将 class 文件加载到 JVM 内存,然后再转化为 class 对象 ...
- JVM探究之 —— 类加载器-双亲委派模型
虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类.实现这个动作的代码模块称为“类加载器 ...
- 【深入理解JVM】类加载器与双亲委派模型 (转)
出处: [深入理解JVM]类加载器与双亲委派模型 加载类的开放性 类加载器(ClassLoader)是Java语言的一项创新,也是Java流行的一个重要原因.在类加载的第一阶段“加载”过程中,需要通过 ...
随机推荐
- Lua虚拟机中的数据结构与栈
Lua虚拟机中的数据结构与栈 来源 https://blog.csdn.net/zry112233/article/details/80828327 由上一篇文章可知解释器分析Lua文件之后生成Pro ...
- Zircon
Zircon 来源 https://github.com/zhangpf/fuchsia-docs-zh-CN/tree/master/docs/the-book 国内镜像源 https://hexa ...
- wrbstrom使用
使用webstrom时遇到Firefox浏览器打不开问题,是webstrom未找到你Firefox的安装路径下面为大家提供解决方法: 文件--->设置--->工具--->web浏览器 ...
- 编写Dockerfile自定义镜像
要求 编写一个Dockerfile自定义centos镜像,要求在容器内部可以使用vim和ifconfig命令,并且登入落脚点为/usr/local 编写Dockerfile FROM centos M ...
- Docker搭建Gitlab代码管理平台
一.Gitlab的安装 宿主机环境: CentOS 7 docker docker-compose 1.查找镜像 docker search gitlab 2.拉取镜像 docker pull git ...
- 测试clang-format的格式化效果
我自己写的业余框架已告一段落,主体功能已完成,剩下的就是优化.第一个要优化的,就是代码格式.我一直是用编辑器写代码的,从之前的UltraEdit到notepad++到sublime text,再到现在 ...
- Flask debug 模式 PIN 码生成机制安全性研究笔记
Flask debug 模式 PIN 码生成机制安全性研究笔记 0x00 前言 前几天我整理了一个笔记:Flask开启debug模式等于给黑客留了后门,就Flask在生产网络中开启debug模式可能产 ...
- sklearn--模型的评价
sklearn.metrics 1.MSE(均方误差)和RMSE(均方根误差),以及score() lr.score(test_x,test_y)#越接近1越好,负的很差 from sklearn.m ...
- 十三:MVC-HTML辅助方法-输出表单
ASP.NET MVC框架内置多个表单相关的HTML辅助方法 HTML辅助方法 说明 Html.BeginForm() 输出<form>标签 Html.CheckBox() 输出<i ...
- RHEL6进入救援模式
1.救援模式 救援模式作用: 更改root密码: 恢复硬盘.文件系统操作 系统无法启动时,通过救援模式启动 2.放入系统光盘,重启从光盘启动: 4.选择语言,默认English就行 5.保持默 ...