JVM 加载机制
1. 加载顺序
jvm 加载顺序为加载,验证,准备,解析,初始化,使用,卸载
1 | class testLoad { |
1. 加载
在硬盘上查找并通过 IO 读入字节码文件,机制为 ** 懒加载 **,即使用才进行加载
2. 验证
验证 testload.class
是否符合规范 ,如开头统一的 4 个字节为 cafe babe
成为 魔数
3. 准备
将类中的静态变量 按类型赋予初值,如 a
在这步中会赋值 0,对象会赋值 null
4. 解析
将类中的 ** 符合引用 ** 解析为 ** 直接引用 **,如 关键字 String
, 方法名 toString
等都为符号,在这一步中会将其转换为内存地址,通过内存地址查询
1 | // 可查询类成员信息 |
5. 初始化
将值赋值给静态变量,如 a
会赋值为 66
2. 双亲委派机制
jvm 中主要分为 4 种类加载器,即 ** 引导类加载器 , ** 扩展类加载器 ** , 应用类加载器 , 自定义加载器 **
1. 类加载器简单介绍
1. 引导类加载器
java 自带的类加载器,主要加载 java 核心 jar 包,由 C++ 实现。
同时会创建 jvm 启动类 Launcher,该类会创建其他类加载器
2. 扩展类加载器
用于加载引入的其他 jar 包
3. 应用类加载器
即当前应用项目的类加载器
4. 自定义类加载器
即重写 **findClass()**,和 loadClass() 的自定义类加载器,主要用于打破 ** 双亲委派机制 **
2. 双亲委派机制流程
即在对一个类加载时,会进行一下操作:
- 应用类加载器查询自己是否已加载此类,若已加载则返回
- 若未加载则委托扩展类加载器加载,查询是否已加载,若已加载则返回
- 若都未加载此类,则委托引导类加载器进行加载,加载成功则返回,失败则委托扩展类加载器加载,
- 若扩展类加载器不能加载则继续委托应用类加载器进行加载
3. 源码分析
主要针对于 Launcher 类进行解析
其中核心函数为 findClass(), loadClass(),defineClass()
Launcher 会创建 appClassLoad
和 extClassLoad
,并将 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)
2. 源码分析
通过重写 **loadClass()**,通过判断路径 name
是否为自定义类,进行分别处理,若为自定义类,则用自定义类加载器,若不是,则按原方法执行。
JVM 中可以有同包名和同类名的类,查看是否为同一类,还得看是否是同一类加载器