【深入理解】Java的类加载过程

📅 2026/6/17 9:42:33
【深入理解】Java的类加载过程
问题概述类加载就是把.class文件的二进制数据读进内存经过校验转换、最终变成JVM能用的Class对象。二进制流不一定非得来自.class文件也可以是字节码工具动态生成的、或者从网络传过来的只要格式对JVM都认。具体流程细节整个类加载流程分为三大阶段加载、连接、初始化。连接又能拆分成验证、准备、解析三步。所以细分下来是5个阶段1加载把二进制流读进内存在方法区生成类的运行时数据结构同时在堆里创建一个class对象作为访问入口。2验证校验二进制流是否符合Class文件规范包括魔数检查、版本号校验、元数据验证、字节码验证、符好引用验证。这一步是为了防止恶意代码搞崩JVM。3准备给类变量static修饰的变量分配内存并设置初始零值。注意这里只是零值比如static int a 123在准备阶段a的值是0不是123。但如果是static final int a 123编译期就确定了准备阶段直接赋值123。4解析把常量池里的符号引用替换成直接引用。符号引用就是一个字符串形式的标识比如Java/lang/Object直接引用是真正的内存地址或偏移量能直接定位到目标。5初始化执行类构造器clinit()方法这时候才真正执行static int a 123这种赋值操作静态代码块就是在这个阶段跑的。准备阶段和初始化的区别publicclassLoadingDemo{// 准备阶段value 0// 初始化阶段value 100privatestaticintvalue100;// 准备阶段就直接赋值 200因为是 final 常量privatestaticfinalintCONSTANT200;static{System.out.println(静态代码块执行value value);}}扩展1类加载的触发时机JVM规范规定了6种情况必须立即对类进行初始化遇到new、getstatic、putstatic、invokestatic这4条字节码指令时。对应的Java代码就是new对象、读取或设置类的静态字段被final修饰的常量除外、调用静态方法使用Java.lang.reflect包对类进行反射调用时初始化子类时发现父类还没初始化先把父类初始化了JVM启动时指定的主类包含main方法对应的那个类JDK7开始的动态语言支持如果MethodHandle实例解析结果是REF_getstatic、REF_putstatic、REF_invokestatic、REF_newInvokeSpecial这四种句柄对应的类要先初始化接口中定义了default方法实现类初始化前要先初始化这个接口2类加载器的层次结构JVM的类加载采用双亲委派模型有三种内置的类加载器1Bootstrap ClassLoader最顶层的加载器C实现的负责加载JAVA_HOME/lib目录下的核心类库。2Extension ClassLoader负责加载JAVA_HOME/lib/ext目录下的扩展类库。JDK9之后改名叫Platform ClassLoader3Application ClassLoader加载classpath下的类也就是我们自己写的代码和引入的第三方jar包