类加载器
类加载器是用来完成类的加载的。
一、JVM中类加载器相关的基本概念
(1)类的加载和卸载
JVM将指定的class文件读取到内存里,并运行该class文件里的java程序的过程,就称之为“类的加载”;
反之,将某个class文件的运行时数据从JVM中移除的过程,就称之为“类的卸载”。
(2)类的生命周期
Java类从被虚拟机加载开始,到卸载出内存为止,它的生命周期包括:
加载(Loading)、
验证(Verification)、
准备(Preparation)、
解析(Resolution)、
初始化(Initialization)、
使用(Using)、
卸载(Unloading)
7个阶段。
其中验证、准备、解析又称为连接(Linking)阶段。
二、类加载过程
类加载过程:Class文件被JVM加载至JVM内存,在内存中验证、准备、解析、初始化之后,形成可以被JVM直接使用的java类型。
类加载过程是在Java程序运行期间完成,虽然会损耗一部分性能,但提高了Java语言的灵活性,体现在动态扩展方面,例如:多态(晚期绑定)。
类加载主要有以下几个步骤:
1、加载:在机载阶段,虚拟机主要完成以下三件事情:
1)通过class文件的路径读取到二进制流。
2)解析二进制流将里面的元数据(类型、常量等)载入到方法区。
3)在java堆中生成对应的java.lang.Class对象。
2、连接
连接过程又分为3步,验证、准备、解析
2.1、验证:判断class文件的合法性
2.2、准备:分配内存,给类的一些字段设置初始值。
比如:
public static int a = 1;在准备阶段a的值会初始化为0,到后面的初始化时才会设置为1.
public final static int b = 1;在准备阶段就会被设置成指定的值,即b在准备阶段设为1。
2.3、解析:将符号引用替换成直接引用。
比如:某个类继承Object,原来的符号引用记录的是“java.lang.object”这个符号,但不能找到该对象在哪。
直接引用就是要找到“java.lang.object”所在的内存地址,建立直接引用关系,这样就方便查询到具体对象。
3、初始化:主要包括执行类构造方法、static变量赋值语句、static{}语句块
需要注意的是:
一个类进行初始化,那么会先初始化其父类,保证父类在子类之前被初始化。
所以在java中初始化一个类,必先初始化java.lang.object,因为所有的java类都继承java.lang.object。
三、类加载器以及双亲委派模式
类加载器有一下几个:
1. 引导类加载器(Bootstrap ClassLoader):在jvm运行时,内嵌在jvm中的一段特殊的用来加载java核心类库的C++代码。
2. 扩展类加载器(Extension ClassLoader):用来加载jvm实现的一个扩展目录,该目录下的所有java类都由此类加载器加载。
3.应用类加载器(Application ClassLoader):又称系统类加载器,主要负责加载程序开发者自己编写的java类。
4. 自定义加载器(Custom ClassLoader):通过java.lang.ClassLoader的子类自定义加载class。
在选用类加载器的时候,先会自下向上检查类是否被加载,
那么从Application ClassLoader 到 Bootstrap ClassLoader 检查是否被加载了(通过findLoaderClass),如果没有加载,则会交给父类,直到没有父类。
如果仍然没有,自顶向下尝试加载类,
那么从Bootstrap ClassLoader 到 App ClassLoader依次尝试加载。
双亲委派模型:
如果一个类加载器收到了某个类的请求,则该类加载器并不会加载该类,而是把这个请求委派给父类加载器,每一个层次类加载器都是如此,因此所有的类加载请求最终都会传送到顶端的启动类加载器;
只有当父类加载器,在其搜索范围内无法找到所需的类,并将该结果反馈给子类加载器,子类加载器会尝试去加载。
这就是双亲委派模型。
备注:
不同的类加载器加载同一类,加载后的对象是完全不一样的。
即使两个类来源于相同的class文件,如果使用不同的类加载器,加载后的对象是完全不同的。
这个不同反应在对象的equals()、isAssignableFrom()、isInstance()等方法的返回结果,包括了使用isntanceof关键字对对象所属关系的判定结果。
为什么使用双亲委派?
一个是安全性,另一个就是性能;(避免重复加载 和 避免核心类被篡改)
破坏双亲委派模式
个人觉得不是重点,但提一下。
例子:Tomcat的WebappClassLoader 就会先加载自己的Class,找不到再委托parent;
双亲模式是默认的模式,但不是必须这么做;
由于用户对程序动态性的追求导致的,这里所说的“动态性”指的是当前一些非常“热门”的名词:代码热替换、模块热部署等,简答的说就是机器不用重启,只要部署上就能用。
举例:
OSGi实现模块化热部署的关键则是它自定义的类加载器机制的实现。每一个程序模块(Bundle)都有一个自己的类加载器,当需要更换一个Bundle时,就把Bundle连同类加载器一起换掉以实现代码的热替换。
本文地址:https://blog.csdn.net/github_37130188/article/details/110262141
希望与广大网友互动??
点此进行留言吧!