JVM类加载机制
JVM 的类加载机制(Class Loading Mechanism)是 Java 虚拟机中一个非常核心的部分,它决定了类从哪里来、怎么加载、什么时候初始化等一系列行为。
类加载整体流程
Java 类的加载过程可以分为五个阶段:
-
加载(Loading): 把 .class 文件读入内存,生成一个 java.lang.Class 对象。由类加载器完成,可能来自本地磁盘、网络、甚至是动态生成的字节码。
-
验证(Verification): 确保字节码符合 JVM 规范,保证运行时安全。检查格式是否正确、语义是否符合规定、权限是否合法等。
-
准备(Preparation): 为类的静态变量分配内存,并设置初始默认值(不是显式赋值)。
-
解析(Resolution): 把常量池中的符号引用转换为直接引用。如:类名、字段名、方法签名 -> 实际地址。
-
初始化(Initialization): 正式执行
<clinit>()方法(类构造器),也就是执行静态变量的初始化和静态代码块。这是类加载过程的最后一步,之后类才可以使用。
类加载器模型(ClassLoader)
JVM 中的类加载器负责把类加载进 JVM,并维持一个”类名-类实例”的映射表。
- Bootstrap ClassLoader(启动类加载器): JVM 内置,用 C++ 实现, JAVA_HOME/lib 核心类,如 java.lang.*
- Extension ClassLoader(扩展类加载器): 加载扩展库,JAVA_HOME/lib/ext
- App ClassLoader(应用类加载器): 最常用,默认加载你的代码,classpath 路径下的类
- 自定义 ClassLoader: 开发者自定义实现,如热部署、解密等
双亲委派机制(Parent Delegation Model)
类加载器在加载类之前,会先把请求交给父类加载器,一直向上委托,直到 Bootstrap。只有当父类加载器无法加载,子加载器才会尝试加载。
机制作用:
- 防止重复加载同一个 .class,通过委托机制向上面去问问,加载过了就不加载了,保证数据安全。
- 保证 .class 不能被篡改,通过委托方式,不会去篡改核心 .class,即使篡改也不会加载。即使加载了也不会同一个 .class 对象了。
类的生命周期
被主动使用时才会触发类加载(尤其是初始化):
- new 实例
- 访问静态变量
- 调用静态方法
- 反射调用
- 子类初始化
类卸载: JVM 不再使用某个类,并且加载它的 ClassLoader 被回收,才可能卸载该类(主要出现在 OSGi、Tomcat 这类容器中)。
Tomcat的类加载机制
Tomcat 的类加载机制相对于 JVM的类加载机制做了一些改变,没有严格的遵从双亲委派机制,也可以说打破了双亲委派机制。
Tomcat 8.5 默认改变了严格的双亲委派机制:
- 首先从 Bootstrap ClassLoader 加载指定的类
- 如果未加载到,则从 /WEB-INF/classes 加载
- 如果未加载到,则从 /WEB-INF/*.jar 加载
- 如果未加载到,则依次从 System、Common、Shared 加载(在这最后一步,遵从双亲委派机制)