JVM 加载机制

1. 加载顺序

jvm 加载顺序为加载,验证,准备,解析,初始化,使用,卸载

1
2
3
4
5
6
7
class testLoad {
static int a = 66;

public String toString() {
return "1111";
}
}

1. 加载

​ 在硬盘上查找并通过 IO 读入字节码文件,机制为 ** 懒加载 **,即使用才进行加载

2. 验证

​ 验证 testload.class 是否符合规范 ,如开头统一的 4 个字节为 cafe babe 成为 魔数

3. 准备

​ 将类中的静态变量 按类型赋予初值,如 a 在这步中会赋值 0,对象会赋值 null

4. 解析

​ 将类中的 ** 符合引用 ** 解析为 ** 直接引用 **,如 关键字 String, 方法名 toString 等都为符号,在这一步中会将其转换为内存地址,通过内存地址查询

1
2
// 可查询类成员信息 
javap-verbose-p testLoad.class

5. 初始化

​ 将值赋值给静态变量,如 a 会赋值为 66

2. 双亲委派机制

​ jvm 中主要分为 4 种类加载器,即 ** 引导类加载器 , ** 扩展类加载器 ** , 应用类加载器 自定义加载器 **

1. 类加载器简单介绍

1. 引导类加载器

​ java 自带的类加载器,主要加载 java 核心 jar 包,由 C++ 实现。
​ 同时会创建 jvm 启动类 Launcher,该类会创建其他类加载器

2. 扩展类加载器

​ 用于加载引入的其他 jar 包

3. 应用类加载器

​ 即当前应用项目的类加载器

4. 自定义类加载器

​ 即重写 **findClass()**,和 loadClass() 的自定义类加载器,主要用于打破 ** 双亲委派机制 **

2. 双亲委派机制流程

​ 即在对一个类加载时,会进行一下操作:

  1. 应用类加载器查询自己是否已加载此类,若已加载则返回
  2. 若未加载则委托扩展类加载器加载,查询是否已加载,若已加载则返回
  3. 若都未加载此类,则委托引导类加载器进行加载,加载成功则返回,失败则委托扩展类加载器加载,
  4. 若扩展类加载器不能加载则继续委托应用类加载器进行加载

3. 源码分析

​ 主要针对于 Launcher 类进行解析

​ 其中核心函数为 findClass(), loadClass(),defineClass()
Launcher 会创建 appClassLoadextClassLoad ,并将 extClassLoad 传入 appClassLoad 中,作为 appClassLoad
的 present 属性的值。
​ 而在 loadClass() 方法中,首先通过 类全路径 查询是否在已加载类中,若查询结果为 null,则会执行 present 的 *
loadClass()* 方法,重复操作,直到 present 属性为 null 时,则会通过 findBootstrapClassOrNull 方法去让
引导类加载器进行查询加载,失败后则执行当前类加载器 的 findClass(), 通过类全路径解析为文件地址进行查找,再通过 *
defineClass()* 进行加载。若查找失败,则跳出 present 的 loadClass() 方法,在 appClassLoad 继续重复 present 中的操作。

4. Tomcat 打破双亲委派

1. 类加载器类型

​ tomcat 中主要分有 CommonClassLoader (最基本类加载器),CatalinaClassLoader(tomcat 私有类加载器),SharedClassLoader(webapp
共享类加载器),WebappClassLoader(webapp 自己私有类加载器),JSP 类加载器 (jsp 热部署,通过替换类加载器实现实时更新 jsp)

image-20220510175659234

2. 源码分析

​ 通过重写 **loadClass()**,通过判断路径 name 是否为自定义类,进行分别处理,若为自定义类,则用自定义类加载器,若不是,则按原方法执行。

JVM 中可以有同包名和同类名的类,查看是否为同一类,还得看是否是同一类加载器