JVM类加载机制
类加载过程
详细链接: https://www.processon.com/view/link/61e31970e0b34d1be7f8ac96
- ClassLoader.loadClass()的类加载过程
- 加载:
从磁盘上将.class的字节码文件加载到JVM内存中去
,此时会做一些简单的校验,校验通过后,会生成一个java.lang.Class对象,作为这个类各种数据再方法区的入口 - 验证: 校验字节码文件的正确性,看看是否符合JVM字节码规范,比如魔数,主次版本号等
- 准备: 将类中的静态变量分配内存并且赋默认值
- 解析: 将符号引用(比如方法名)转变为直接引用(JVM内存地址),这就是所谓的
静态链接
的过程
- 静态链接: 类加载期间,将符号引用转变为直接引用
- 动态链接: 程序运行期间,将符号引用转变为直接引用,比如方法中调用方法,JVM解析的时候会对被调用的方法名指向一个内存地址,通过这个地址找到对应的方法,
最常见的就是多态,有不同的实现,只有到运行的时候才知道
- 初始化: 对类的静态变量初始化为指定值,执行静态代码块
- 类被加载到方法区中包含
- 运行时常量池
- 类型信息
- 字段信息
- 方法信息
- 类加载器的引用
- 对应class实例的引用
注意
: 类加载机制是懒加载,部署war包使用时才会加载,静态代码块比构造函数先执行,类没加载完,不会调用构造函数
类加载器
-
引导类加载器(BootStrap): 加载jre/lib目录下的核心类库
-
拓展类加载器(Ext): 加载支撑JVM运行
的jre/lib/ext目录下的jar
-
应用程序类加载器(App): 加载classpath路径下的类包,主要是加载用户自己写的类
-
自定义类加载器: 加载用户自定义路径下的类。特殊需要的时候才自定义类加载器,比如Tomcat会自定义类加载器,默认是应用程序加载器
-
sum.misc.Launcher
可以理解为所有类的父类加载器,C++先初始化Launcher类后,在通过此类生成拓展类、应用程序类加载器等
-
问题
: 为什么引导类加载器不是Launcher类生成的
- 创建JVM启动器实例: sun.misc.Launcher,此类为单例模式,保证只有一个实例
- Launcher类创建两个类,ExtClassLoader和AppClassLoader
- JVM默认使用Launcher的ClassLoader方法返回的加载器AppClassLoader的实例加载我们的应用程序
双亲委派机制
- 当应用程序类加载器加载某类时,如果没有找到,就会向上让扩展类加载器去加载
- 如果扩展类加载器也没有该类,就会继续向上让引导类加载器去加载
- 如果引导类加载器也没有该类,那么就向下继续委托
- 如果都没有找到,那么就让应用程序类加载器去加载
为什么要先用应用程序类加载器去加载代码,而不是直接用引导类加载器
- 因为大部分的类都是用户自己定义编写的,也就是大部分的类文件都存放在应用程序类加载器的指定类路径下,如果不从这里开始会导致效率降低
什么是双亲委派机制
- 双亲委派机制就是加载某个类时先委托父加载器去寻找目标类,找不到再委托上层父加载器,如果所有父加载器都没有自己类路径下找到目标类,那么就会交给最开始的类加载器去加载该目标类
为什么要设计双亲委派机制
- 双亲委派机制可以
避免类的重复加载
沙箱安全机制
,保证核心类不被篡改
全盘委托机制
- 全盘委托是指当一个ClassLoder装载一个类时,除非显示的使用另外一个ClassLoder,该类所依赖及引用的类也由这个ClassLoder载入
如何自定义类加载器
代码思路
- 继承java.lang.ClassLoader类
- 重写ClassLoader.findClass()方法,此方法通过IO读取自定类文件路径
- 重写ClassLoader.loadClass()方法,加载类
打破双亲委派机制
- 加载一个类,不使用父加载器,直接由自定义类加载器加载类
代码思路
- 重写ClassLoader.loadClass()方法后
- 在里面调用父加载器的方法处添加判断逻辑,如果是自定义类路径,就调用自定义的findClass方法
- 对于一个web容器来说,可能需要部署多个应用程序,不同的应用程序可能依赖相同类库的不同版本,但是双亲委派机制已经实现了避免重复类加载,因此要
保证每个版本的类库是独立的,要保证相互隔离
- 部署在
同一个web容器中相同类库的版本可以共享
,否则多个应用程序此时都是同一类库的版本,会导致进行加载多次,浪费资源 - 处于安全方面考虑,
Tomcat也有自己的类库,也需要进行隔离处理
,否则会导致与应用程序的类库混淆 - 每个jsp对应一个类加载器,这样是为了
方便热部署
Tomcat这种类加载机制是否违背了双亲委派机制
- 很显然是的,Tomcat为了实现隔离性,每个webappClassLoader加载自己目录下的类文件,不会传递给父加载器,打破了双亲委派机制
- 首先自己尝试去加载某类,如果找不到再代理给父加载器,其目的是优先加载web应用自己定义的类,它父类加载器就是AppClassLoader
- web应用是通过Class.forName()调用交给系统类加载器的,因为Class.forName的默认类加载器是系统类加载器
Tomcat怎么实现热加载
- 单独用一个线程时刻去监听jsp文件路径下文件夹的变动,比如说1s监听一次,如果有变动,把之前的类加载器置空,再重新赋值最新的类加载器,之前的等待gc回收
Tomcat类加载图