深入理解JVM07--虚拟机类加载机制--类加载器、双亲委派模型
2016-05-21 23:59
633 查看
本文是基于周志明的《深入理解Java虚拟机》
在加载阶段,java虚拟机需要完成以下3件事:
a.通过一个类的全限定名来获取定义此类的二进制字节流。
b.将定义类的二进制字节流所代表的静态存储结构转换为方法区的运行时数据结构。
c.在java堆中生成一个代表该类的java.lang.Class对象,作为方法区数据的访问入口。
虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到Java虚拟机外部实现,以便让应用程序自己决定如何去获取所需要的类,实现这个动作的代码块被称为“类加载器”
①.一种是启动类加载器(Bootstrap ClassLoader),该类加载器使用C++语言实现,属于虚拟机自身的一部分。
②.另外一种就是所有其它的类加载器,这些类加载器是由Java语言实现,独立于JVM外部,并且全部继承自抽象类java.lang.ClassLoader。
系统提供的类加载器主要有下面3个:
1).启动类加载器(Bootstarp ClassLoader)
这个ClassLoader由JVM自己控制。主要加载JVM自身工作需要的类:将%JAVA_HOME%\lib路径下或-Xbootclasspath参数指定路径下的、能被虚拟机识别的类库(仅按照文件名识别,如:rt.jar,名字不符合的类库不会被加载)加载至虚拟机内存中
启动类加载器无法被Java程序直接引用
2).扩展类加载器(Extension ClassLoader)
该类加载器由sun.misc.Launcher类的静态内部类ExtClassLoader实现。
负责加载java.ext.dirs参数(默认值是%JAVA_HOME%\jre\lib\ext,可由VM参数-Djava.ext.dirs指定)指定路径中的所有类库,开发者可以直接使用扩展类加载器。
3).应用程序类加载器(Application ClassLoader)
该类加载器由sun.misc.Launcher$AppClassLoader实现。由于这个类加载器是ClassLoader中的getSystemClassLoader()方法的返回值,也称为系统类加载器。
负责加载用户类路径(Classpath)上所指定的类库,开发者可以直接使用该类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。
注意:上述三个JDK提供的类加载器虽然是父子类加载器关系,但是没有使用继承,而是使用了组合关系。
4).自定义类加载器
除了系统提供的类加载器之外,开发人员可以通过继承java.lang.ClassLoader类并重写该类的findClass方法的方式实现自己的类加载器
从JDK1.2开始,java虚拟机规范推荐开发者使用双亲委派模式(ParentsDelegation Model)进行类加载,其加载过程如下:
1).如果一个类加载器收到了类加载请求,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器去完成,每一个层次的加载器都是如此
2).因此所有的类加载请求都会传给顶层的启动类加载器,只有当父加载器反馈自己无法完成该加载请求(该加载器的搜索范围中没有找到对应的类)时,子加载器才会尝试自己去加载。
3).如果连最初发起类加载请求的类加载器也无法完成加载请求时,将会抛出ClassNotFoundException,而不再调用其子类加载器去进行类加载。
双亲委派模式的类加载机制的优点是java类它的类加载器一起具备了一种带优先级的层次关系,越是基础的类,越是被上层的类加载器进行加载,保证了java程序的稳定运行。
例如java.lang.Object类,无论哪个类加载器去加载该类,最终都是由启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都是同一个类。否则的话,如果不使用该模型的话,如果用户自定义一个java.lang.Object类且存放在classpath中,那么系统中将会出现多个Object类,应用程序也会变得很混乱。如果我们自定义一个rt.jar中已有类的同名Java类,会发现JVM可以正常编译,但该类永远无法被加载运行。
在rt.jar包中的java.lang.ClassLoader类中,我们可以查看类加载实现过程的代码,具体源码如下:
在加载阶段,java虚拟机需要完成以下3件事:
a.通过一个类的全限定名来获取定义此类的二进制字节流。
b.将定义类的二进制字节流所代表的静态存储结构转换为方法区的运行时数据结构。
c.在java堆中生成一个代表该类的java.lang.Class对象,作为方法区数据的访问入口。
虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到Java虚拟机外部实现,以便让应用程序自己决定如何去获取所需要的类,实现这个动作的代码块被称为“类加载器”
1、类与类加载器
类加载器的作用:首先类加载器可以实现最本质的功能即类的加载动作。同时,它还能够结合java类本身来确定该类在Java虚拟机中的唯一性。用通俗的话来说就是:比较两个类是否相等,只有这两个类是由同一个类加载器加载才有意义。否则,即使这两个类是来源于同一个Class文件,只要加载它们的类加载器不同,那么这两个类必定不相等。2、双亲委派模型
虚拟机的角度来说,只存在两种不同的类加载器:①.一种是启动类加载器(Bootstrap ClassLoader),该类加载器使用C++语言实现,属于虚拟机自身的一部分。
②.另外一种就是所有其它的类加载器,这些类加载器是由Java语言实现,独立于JVM外部,并且全部继承自抽象类java.lang.ClassLoader。
系统提供的类加载器主要有下面3个:
1).启动类加载器(Bootstarp ClassLoader)
这个ClassLoader由JVM自己控制。主要加载JVM自身工作需要的类:将%JAVA_HOME%\lib路径下或-Xbootclasspath参数指定路径下的、能被虚拟机识别的类库(仅按照文件名识别,如:rt.jar,名字不符合的类库不会被加载)加载至虚拟机内存中
启动类加载器无法被Java程序直接引用
2).扩展类加载器(Extension ClassLoader)
该类加载器由sun.misc.Launcher类的静态内部类ExtClassLoader实现。
负责加载java.ext.dirs参数(默认值是%JAVA_HOME%\jre\lib\ext,可由VM参数-Djava.ext.dirs指定)指定路径中的所有类库,开发者可以直接使用扩展类加载器。
3).应用程序类加载器(Application ClassLoader)
该类加载器由sun.misc.Launcher$AppClassLoader实现。由于这个类加载器是ClassLoader中的getSystemClassLoader()方法的返回值,也称为系统类加载器。
负责加载用户类路径(Classpath)上所指定的类库,开发者可以直接使用该类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。
注意:上述三个JDK提供的类加载器虽然是父子类加载器关系,但是没有使用继承,而是使用了组合关系。
4).自定义类加载器
除了系统提供的类加载器之外,开发人员可以通过继承java.lang.ClassLoader类并重写该类的findClass方法的方式实现自己的类加载器
从JDK1.2开始,java虚拟机规范推荐开发者使用双亲委派模式(ParentsDelegation Model)进行类加载,其加载过程如下:
1).如果一个类加载器收到了类加载请求,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器去完成,每一个层次的加载器都是如此
2).因此所有的类加载请求都会传给顶层的启动类加载器,只有当父加载器反馈自己无法完成该加载请求(该加载器的搜索范围中没有找到对应的类)时,子加载器才会尝试自己去加载。
3).如果连最初发起类加载请求的类加载器也无法完成加载请求时,将会抛出ClassNotFoundException,而不再调用其子类加载器去进行类加载。
双亲委派模式的类加载机制的优点是java类它的类加载器一起具备了一种带优先级的层次关系,越是基础的类,越是被上层的类加载器进行加载,保证了java程序的稳定运行。
例如java.lang.Object类,无论哪个类加载器去加载该类,最终都是由启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都是同一个类。否则的话,如果不使用该模型的话,如果用户自定义一个java.lang.Object类且存放在classpath中,那么系统中将会出现多个Object类,应用程序也会变得很混乱。如果我们自定义一个rt.jar中已有类的同名Java类,会发现JVM可以正常编译,但该类永远无法被加载运行。
在rt.jar包中的java.lang.ClassLoader类中,我们可以查看类加载实现过程的代码,具体源码如下:
//双亲委派模型的实现源码 protected synchronized Class<?> loadClass(String name, Boolean resolve) throws ClassNotFoundException{ //1、首先检查请求的类是否已经被加载过 Class c = findLoadedClass(name); if(c == null){ try{ if(parent != null){//2、如果没有则调用父加载器的loadClass()方法 c = parent.loadClass(name, false); //3、如果父加载器为空则默认使用启动类加载器作为父加载器 } else{ c = findBootstrapClassOrNull(name); } }catch(ClassNotFoundException e){ //4、如果父类加载器加载失败,则先抛出ClassNotFoundException } //5、然后再调用自己的findClass()方法进行加载 if(c == null){ c = findClass(name); } } if(resolve){ resolveClass(c); } return c; }
相关文章推荐
- C#委托举例
- 《Linux命令行与shell脚本》笔记--第4章:更多的bash shell命令·
- 如何让PHP开启CURL支持?
- ASM的磁盘组无法挂载解决办法
- 判断webview是否彻底加载完成
- HDU 2054 又见GCD(水题??)
- VS2010 MFC C++ 弹出窗口
- 如何在redhat/centos linux服务器中安装git仓库服务
- 如何解决打开xcode项目是遇到的“cannot be opened because the project file cannot be parsed.”问题?
- Java中解析html代码(四)
- JAVA虚拟机学习1---OpenJDK的编译
- java中模拟浏览器访问网页(三)
- CentOS 7下安装Mysql 5.7
- 1114 - Easily Readable
- 树莓派GPIO使用大全
- rgbdslam_v2实践
- Mysql 拿指定经纬度与数据库多条经纬度进行距离计算
- 如何编写和应用Java的自定义异常类
- Tomcat:IOException while loading persisted sessions: java.io.EOFException
- LINQ----1