继续学习 jvm 的相关知识,前一个星期都在出差,在现场事情是在太多,没坚持学习,出差回来之后要补上。
我已经学习了jvm的内存结构和垃圾回收机制。
接下来应该学习jvm的类加载机制了。
主要是学习jvm如何使用管理class对象。
java代码编译成字节码运行jvm虚拟机上,.java文件编译成.class文件。
那么.class文件是如何变成jvm里面的class对象的?
class对象初始化
从.class文件变成jvm里面的class对象,步骤如下:
- 加载:.class文件的字节流读取进内存,生成class对象
- 验证:检查字节码是否有问题
- 准备:分配给静态变量内存,并且赋值,值是jvm的默认值,不是代码里面写的初始值。
- 解析:将符号引用换成直接引用。
- 初始化:执行static代码块,给静态变量赋值初始值。这里是初始值就是代码里面给的初始值。
注意:验证、准备、解析,合起来叫“连接”。
双亲委派模式
jvm的类加载有层次关系,加载类的时候,会先向父加载器派这个加载任务,如果父加载器加载不了,才会由他自己加载。
这样做的目的:
- 安全。避免核心类库被篡改,比如我自己写了一个java.land.String,覆盖jdk的String库。因为向上委派,jvm加载的是jdk的String库。
- 保证一个类在jvm中唯一,防止重复加载和类型转换异常。
SpringBoot的双亲委派模式
Spring Boot没有打破双亲委派,而是在模型的框架内做扩展。
这样实现了两个功能:
- 胖jar
- 热重启
胖 Jar = 代码 + 所有依赖的第三方 Jar + 嵌入的 Web 容器 + Spring Boot 启动器,全部打成一个可独立运行的 Jar 包。
胖jar
胖 Jar 的 LaunchedURLClassLoader
它继承 URLClassLoader,父加载器仍是 App ClassLoader。核心改变不是委托顺序,而是类路径的来源:它能从 BOOT-INF/classes/ 和 BOOT-INF/lib/ 下加载类。加载一个类时还是先问父加载器,父加载器找不到这些嵌套路径,自然由它自己从胖 Jar 中加载。本质只是扩展了搜索范围。
热重启
DevTools 的“伪热部署”(快速重启)这是类加载器替换与懒加载理念的漂亮结合。DevTools 使用两个类加载器:
base ClassLoader:加载不变的第三方库。 restart ClassLoader:加载项目中的业务类。
改动代码后,它丢弃旧的 restart ClassLoader 并新建一个,base ClassLoader 保持不动。因为只重新加载项目类,重启速度极快。
初始化时机在这里体现为:只有被用到的业务类才会在 restart ClassLoader 中初始化,完美契合懒加载的高效。