JVM类加载机制

JVM 的类加载机制(Class Loading Mechanism)是 Java 虚拟机中一个非常核心的部分,它决定了类从哪里来、怎么加载、什么时候初始化等一系列行为。

类加载整体流程

Java 类的加载过程可以分为五个阶段:

  1. 加载(Loading): 把 .class 文件读入内存,生成一个 java.lang.Class 对象。由类加载器完成,可能来自本地磁盘、网络、甚至是动态生成的字节码。

  2. 验证(Verification): 确保字节码符合 JVM 规范,保证运行时安全。检查格式是否正确、语义是否符合规定、权限是否合法等。

  3. 准备(Preparation): 为类的静态变量分配内存,并设置初始默认值(不是显式赋值)。

  4. 解析(Resolution): 把常量池中的符号引用转换为直接引用。如:类名、字段名、方法签名 -> 实际地址。

  5. 初始化(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 加载(在这最后一步,遵从双亲委派机制)