JVM类加载机制

张开发
2026/5/5 10:56:53 15 分钟阅读

分享文章

JVM类加载机制
在 JVM 中类加载器ClassLoader负责将磁盘上的.class文件读取到内存元空间中。Java 采用的是一种层级结构不同的加载器负责加载不同路径下的类库。1. 四种主要的类加载器从 JDK 8 到 JDK 9模块化之后类加载器的名称和职责稍有变化但逻辑核心是一致的① 启动类加载器 (Bootstrap ClassLoader)地位它是加载器中的“老祖宗”由C编写嵌套在 JVM 内部。职责负责加载 Java 的核心类库如$JAVA_HOME/lib目录下的rt.jar、resources.jar等。特点你在 Java 代码中无法直接获取它返回通常为null。它加载的是像java.lang.Object、java.lang.String这样最基础的类。② 扩展类加载器 (Extension ClassLoader) / 平台类加载器 (Platform ClassLoader)JDK 8叫 Extension ClassLoader负责加载$JAVA_HOME/lib/ext目录下的扩展类库。JDK 9由于模块化改名为Platform ClassLoader。它负责加载 Java 平台的一些特定模块如java.sql、java.xml等。③ 应用程序类加载器 (Application ClassLoader)别名系统类加载器 (System ClassLoader)。职责负责加载用户类路径ClassPath上指定的类库。特点这是我们在开发中最常用的加载器如果你没有自定义加载器你写的代码默认都由它加载。④ 自定义类加载器 (Custom ClassLoader)实现通过继承java.lang.ClassLoader类并重写findClass方法实现。用途加密保护加载加密过的.class文件并在内存中解密。非标准来源从数据库、网络甚至是云端加载类。热部署/隔离像 Tomcat 这种 Web 容器会为每个 Web 应用创建独立的加载器实现应用间的类隔离。2. 双亲委派模型 (Parent Delegation Model)这是 Java 类加载器的核心工作机制。当一个类加载器收到加载请求时它不会自己先去加载而是向上委托把请求委派给父类加载器一直委派到顶层的Bootstrap ClassLoader。向下尝试只有当父加载器反馈自己无法加载在它的搜索范围没找到时子加载器才会尝试自己去加载。为什么要有这个模型核心作用安全性防止核心 API 被篡改。如果没有这个机制你写一个java.lang.String类并让 ApplicationClassLoader 加载那系统里就会有两个 String。有了委派机制系统永远优先使用 Bootstrap 加载的官方版。避免重复加载保证了在整个 JVM 范围内同一个类只会被加载一次。3.类加载过程JVM 的类加载过程是将磁盘上的.class字节码文件转化成元空间Metaspace中运行数据结构的全过程。这个过程可以细分为五个阶段加载、验证、准备、解析、初始化。1. 加载 (Loading)这是类加载的第一步主要完成三件事获取二进制流通过类的全限定名如java.lang.String找到对应的字节码文件。转化结构将字节码中的静态存储结构转化为方法区元空间的运行时数据结构。生成 Class 对象在堆中生成一个代表该类的java.lang.Class对象作为访问元空间中这些数据的入口。2. 链接 (Linking)链接阶段负责将类的二进制数据合并到 JVM 的运行时状态中它包含三个小阶段① 验证 (Verification)目的确保加载的类符合 JVM 规范不会危害虚拟机安全。内容检查文件格式魔数CAFEBABE、元数据验证、字节码验证、符号引用验证等。② 准备 (Preparation)目的为类变量static修饰的变量分配内存并设置初始零值。关键点此时不会执行你的赋值逻辑。例如public static int value 123;在准备阶段value的值是0而不是123。例外如果是static final修饰的常量由于是常量准备阶段就会直接赋予代码中的真实值。③ 解析 (Resolution)目的将常量池内的符号引用替换为直接引用。内容比如代码中调用了methodA()在字节码里只是一个字符串符号解析阶段会将其指向该方法在内存中的实际地址偏移量。3. 初始化 (Initialization)这是类加载过程的最后一步也是真正开始执行 Java 代码的阶段。核心逻辑执行类构造器clinit方法的过程。动作合并所有静态变量的赋值动作和static { ... }静态代码块。按语句在源文件中出现的顺序执行。父类优先JVM 会保证在子类的clinit执行前父类的clinit已经执行完毕。4.使用使用类静态方法或创建对象5.卸载JVM 卸载类Class Unloading是一个比对象回收Object GC严苛得多的过程。在元空间Metaspace中一个类只有在同时满足以下三个条件时才会被垃圾回收器回收。1. 条件一该类所有的实例都已被回收要求在 Java 堆中已经不存在该类及其任何派生子类的任何实例对象。逻辑只要还有一个活着的实例类定义的“模板”就必须留在元空间里否则实例就失去了结构支撑。2. 条件二加载该类的 ClassLoader 已被回收要求负责加载这个类的ClassLoader实例本身必须已经被 GC 回收。为什么这一条最难对于启动类加载器 (Bootstrap)、平台类加载器 (Platform)和应用程序类加载器 (App)它们在 JVM 运行期间几乎永远不会被回收。因此我们编写的普通业务类在正常的程序运行中基本不可能被卸载。场景类卸载通常只发生在频繁使用自定义类加载器的场景如OSGi或热部署框架动态加载一个插件卸载插件时销毁对应的加载器。JSP 引擎每次 JSP 修改后Tomcat 可能会抛弃旧的加载器创建新的来重新加载。3. 条件三该类对应的java.lang.Class对象没有在任何地方被引用要求无法在任何地方通过反射Reflection访问该类的方法或字段。细节如果你的代码里还存着Class? clazz User.class;这种强引用或者有线程正在执行该类的方法那么它就不是死透的。类卸载条件汇总表维度检查项判定标准实例层堆内存 (Heap)实例总数 0 且被 GC 清理加载层类加载器 (ClassLoader)加载器实例已被回收引用层反射与根搜索 (GC Roots)无法通过任何路径触达该类的Class对象6. 什么时候会触发“初始化”JVM 采用的是懒加载机制只有在“主动使用”一个类时才会触发初始化使用new关键字创建实例。访问或修改类的静态变量非final。调用类的静态方法。使用java.lang.reflect进行反射调用。初始化一个类时发现其父类还没初始化则先触发父类初始化。虚拟机启动时的执行主类包含main方法的那个类。

更多文章