JVM的类加载机制

发布时间:2026/6/9 10:36:33

JVM的类加载机制 JVM的类加载机制是Java“一次编写到处运行”和动态性的基石。它的核心任务就是找到并验证字节码文件.class将其定义成JVM能直接使用的Java类。简单来说这个过程由三大部分组成加载、连接、初始化。连接又可以细分为验证、准备、解析。一、类加载器的体系结构双亲委派模型这是JVM类加载机制最核心的设计。JVM不会用一个“万能加载器”而是使用一组有层次关系的加载器。启动类加载器 (Bootstrap ClassLoader) ↑ (父加载器实际是C实现Java中为null) 扩展类加载器 (Extension ClassLoader) ↑ (父加载器为 Bootstrap) 应用程序类加载器 (Application ClassLoader) ↑ (父加载器为 Extension) 自定义类加载器 (Custom ClassLoader)启动类加载器加载JAVA_HOME/lib目录下的核心类库比如rt.jar中的java.lang.*。它是最顶层的没有父加载器。扩展类加载器加载JAVA_HOME/lib/ext目录下的扩展类库。应用程序类加载器加载用户类路径CLASSPATH下的类也就是你编写的.class文件。自定义类加载器开发者可以继承ClassLoader类实现自定义的加载逻辑比如从网络、数据库或加密文件中加载类。双亲委派模型的工作流程当一个类加载器比如应用程序类加载器收到加载一个类的请求时它不会自己立即去加载而是向上委派先将请求委派给它的父加载器扩展类加载器。递归检查父加载器同样会继续向上委派给自己的父加载器启动类加载器直到最顶层的启动类加载器。尝试加载每个父加载器尝试在自己的加载路径中查找并加载这个类。向下查找如果所有父加载器都无法加载才会回到最初发起请求的子加载器让它自己去类路径里查找并加载。这样做的好处避免重复加载父加载器加载过的类子加载器不会再次加载。保证核心类库的安全你无法自己写一个java.lang.Object类去替换系统的。因为当请求加载java.lang.Object时双亲委派会一直向上委派给启动类加载器它找到并加载了正确的Object后应用程序类加载器就不会再加载你写的“假”类了。二、类的生命周期加载、连接、初始化一个类从被加载到内存到卸载出内存完整的生命周期包括7个阶段。其中加载、验证、准备、初始化、卸载这5个阶段的顺序是确定的。1. 加载LoadingJVM要做三件事通过一个类的全限定名如java.lang.String获取其二进制字节流来源可以是.class文件、jar包、网络、甚至动态生成。将这个字节流所代表的静态存储结构转化为方法区元空间的运行时数据结构。在内存的堆中生成一个代表这个类的java.lang.Class对象作为方法区这个类各种数据的访问入口。加载是类加载器工作的主要阶段并且可以结合“双亲委派模型”来理解。2. 连接Linking分为三步其中解析阶段有时可能在初始化之后才开始。验证Verification确保.class字节流符合JVM规范不会危害JVM安全。检查项很多文件格式、元数据、字节码、符号引用等。这是类加载机制安全防护的第一关。准备Preparation为类变量static修饰的变量在方法区中分配内存并设置默认初始值零值。public static int value 123在准备阶段后value的值是0而不是123。123是在后面的初始化阶段才赋值的。如果变量是static final常量情况不同。例如public static final int value 123编译器会在准备阶段就直接给value赋值为123。解析Resolution将常量池内的符号引用替换为直接引用。符号引用字面上的引用例如一个类中调用了另一个类的method()在字节码里就是com/example/OtherClass.method这样一串文字。直接引用指向目标的内存地址或者相对于方法区某个位置的偏移量。一旦找到具体的内存地址后面的调用就是真正的内存访问了。3. 初始化Initialization这是类加载过程的最后一步。JVM会执行类构造器clinit()方法为类变量赋程序员定义的初始值并执行静态代码块。clinit()方法由编译器自动收集类中所有类变量的赋值动作和静态语句块中的语句合并产生。对于public static int value 123在初始化阶段value才会真正被赋值为123。JVM 保证在首次主动使用一个类时才会初始化它。主动使用的情况包括new一个对象访问或调用一个类的静态方法/静态字段final常量除外反射调用初始化一个类时其父类尚未初始化则先初始化父类主类包含main方法的类一个完整的例子假设运行new MyApp()加载应用程序类加载器收到请求向上委派最终由应用程序类加载器自己找到MyApp.class加载成Class对象。连接验证检查MyApp.class格式是否正确。准备为MyApp的静态变量分配内存并设零值比如static int count 10此时count0。解析将MyApp内部引用的System.out等符号解析成真正的内存地址。初始化执行静态代码块和静态变量赋值count被赋值为10。如果父类未初始化先递归初始化父类。完成现在可以安全地创建MyApp实例了。理解类加载机制是排查ClassNotFoundException、NoClassDefFoundError以及实现热部署、字节码增强等技术的基础。如果你还想继续了解类的卸载条件或者自定义类加载器如何实现我可以进一步为你讲解。

相关新闻