Java程序运行需经编译、加载、链接、初始化和执行五阶段:1. javac将.java编译为.class字节码;2. ClassLoader按需加载字节码到元空间;3. 链接含验证、准备(设默认值)、解析(符号引用转直接引用);4. 初始化执行<clinit>方法;5. 使用后满足条件时卸载类。

Java程序从写完源代码到最终在JVM中运行,经历编译、加载、链接、初始化和执行五个关键阶段。整个过程由JDK工具链与JVM协同完成,不是简单的“写完就跑”,而是一套有严格时序和职责分离的机制。
1. 编译:.java → .class(字节码生成)
Java源文件(.java)通过javac编译器转换为平台无关的字节码文件(.class)。这一步不生成机器码,只做语法检查、语义分析、常量池填充和字节码生成。
- 编译器不会执行任何代码逻辑,也不会触发类的初始化(比如static块或静态变量赋值)
- 泛型被擦除,注解默认不保留(除非声明@Retention(RetentionPolicy.CLASS))
- 生成的.class文件包含魔数(0xCAFEBABE)、版本号、常量池、字段/方法表、属性表等结构
2. 类加载:把.class读入JVM内存(ClassLoader参与)
类加载不是一次性全部加载,而是按需触发——当首次主动使用某个类时(如new实例、调用静态方法、反射访问等),JVM启动类加载子系统,通过ClassLoader体系将字节码载入方法区(JDK 8+为元空间)。
- 遵循双亲委派模型:AppClassLoader → ExtClassLoader → BootstrapClassLoader,避免重复加载和核心类被篡改
- 加载阶段只负责“读入二进制流+生成Class对象”,不校验、不准备、不初始化
- 自定义ClassLoader可突破双亲委派(如OSGi、热部署框架),但需谨慎处理委托边界
3. 链接:验证、准备、解析三步落地
链接是连接字节码与JVM运行时的关键环节,分为三个连续子阶段:
立即学习“Java免费学习笔记(深入)”;
- 验证:确保字节码符合JVM规范(如方法调用是否合法、栈帧是否溢出),防止恶意或错误代码危害虚拟机安全
- 准备:为类变量(static字段)分配内存并设默认值(如0、null、false),注意:不执行static块或显式赋值语句
- 解析:将常量池中的符号引用(如类名、方法名、字段名)替换为直接引用(内存地址或偏移量),涉及类、接口、字段、方法、方法类型、方法句柄等的解析
4. 初始化:执行类构造器<clinit>(真正开始运行Java逻辑)
这是类生命周期中第一个真正执行Java代码的阶段。JVM会执行类的<clinit>方法(由编译器自动收集所有static变量赋值动作和static代码块合并而成)。
- 初始化按继承关系自上而下进行:先初始化父类,再初始化子类
- 只有6种情况会触发初始化(JVM规范明确):new、读写静态字段(非final常量)、调用静态方法、反射、初始化子类(父类先触发)、main方法所在类
- 接口初始化不同:只有在首次使用其定义的静态字段时才初始化,且不要求先初始化父接口
5. 使用与卸载:运行期行为与资源回收
类初始化完成后,即可创建实例、调用方法、访问字段——进入常规运行阶段。而类卸载仅发生在该类的ClassLoader被回收,且该类无任何实例、无引用、无子类且未被反射使用时,由GC在元空间回收其元数据。
- 普通应用中,系统类加载器加载的类几乎永不卸载;Web容器(如Tomcat)通过自定义ClassLoader实现模块级热部署,依赖类卸载能力
- 类对象(Class<?>)本身是Java对象,存于堆中,可通过getClassLoader()追溯其加载者
- 运行时动态生成类(如CGLIB、ASM、Lambda metafactory)也走相同流程,只是字节码来源不是磁盘文件

















