类加载与JVM内存区域紧密协同:加载阶段将字节码载入方法区(元空间)并创建Class对象于堆;验证、准备阶段操作方法区中的类结构和static变量;解析、初始化阶段涉及方法区、堆、栈协同,完成符号引用解析和static赋值;类卸载需满足实例、ClassLoader、Class对象均不可达。

类加载过程和JVM内存区域不是两个孤立环节,而是紧密咬合的执行链条:类加载的每一步,都在为特定内存区域填充内容;而每个内存区域的存在,又决定了类加载各阶段能做什么、存什么。
加载阶段 → 方法区(元空间)与堆
类加载的第一步是“加载”,即读取.class字节码并转为运行时数据结构。这个过程的核心落点是方法区(JDK 8+中为元空间)——它存放类的结构信息:字段、方法、常量池、接口列表等。同时,JVM会在堆中创建一个java.lang.Class对象,作为该类在方法区数据的访问入口。注意:Class对象本身在堆里,但它指向的是方法区里的类型元数据。
- 常量池(静态常量池)随class文件一并载入方法区;运行时常量池也在方法区中动态扩展
- 字符串常量池在JDK 7后从永久代移到堆中(但逻辑上仍属运行时常量池管理范畴)
- 类的静态变量(static字段)也分配在方法区(元空间),而非堆
验证与准备阶段 → 方法区为主战场
验证确保字节码安全合规,准备则为类变量(static变量)分配内存并设默认值(如int为0、引用为null)。这两步操作的对象几乎全部落在方法区:校验目标是方法区中刚载入的类结构,准备动作也是直接在方法区为static字段开辟空间。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- final static常量(编译期确定)在准备阶段就直接赋值;普通static变量只赋默认值,初始化阶段才真正赋值
- 此时不会为实例变量分配内存——它们属于对象实例,等new时才在堆中分配
解析与初始化阶段 → 涉及堆、栈、方法区协同
解析将符号引用转为直接引用(比如把“调用某个方法”从名字查表转成内存地址),主要更新方法区中的引用关系。初始化则执行<clinit></clinit>方法,给static变量赋实际值、执行static代码块——这需要栈来执行字节码,也可能触发其他类加载(进而递归填充对应方法区)。
立即学习“Java免费学习笔记(深入)”;
- 初始化过程中若用到new,会触发对象创建:对象实例分配在堆,其引用存于当前线程的虚拟机栈帧中
- 每个线程有自己的虚拟机栈和程序计数器(线程私有),而方法区、堆是所有线程共享的
- 本地方法栈用于native方法,不参与Java类加载主流程,但可能间接影响类初始化(如System.loadLibrary)
类加载完成 ≠ 内存布局终结
类加载五阶段结束后,类型元数据稳定驻留在方法区,Class对象常驻堆中。但后续运行中,对象不断在堆中创建销毁,方法调用在栈中推入弹出,字符串可能进入字符串常量池(堆内专门区域),GC也会对堆和方法区(元空间)分别回收——例如无用类卸载会清理方法区,同时释放对应的Class对象(若无强引用)。
- 类卸载的前提:该类所有实例已被回收、ClassLoader实例不可达、Class对象无引用
- 元空间虽使用本地内存,但仍受-XX:MaxMetaspaceSize限制,超限会触发Full GC甚至OOM
- 堆中对象的生命周期独立于类加载,但对象所属的类必须已加载且初始化完成

















