准备阶段为类变量分配内存并设零值,final static基本类型和字符串常量直接赋代码值;JDK 7前存永久代,JDK 8起随Class对象存堆中,元空间仅存元数据。

准备阶段是 JVM 类加载过程中的关键一环,核心任务是为类变量(即 static 变量)分配内存并设置初始值,但不涉及实例变量——后者在对象创建时才随对象一起分配在堆中。
类变量的内存分配位置
从逻辑上讲,类变量分配在方法区(Method Area);但实际实现取决于 JDK 版本:
- JDK 7 及之前:类变量存放在永久代(PermGen),属于方法区的一部分
- JDK 8 起:永久代被元空间(Metaspace)取代,类变量不再存于元空间,而是随 Class 对象一起分配在 Java 堆中
- 注意:元空间本身只存储类的元数据(如运行时常量池、字段/方法信息等),static 变量的值实体不在元空间
初始值怎么设?看是否是 final static
准备阶段设置的“初始值”不是代码里写的值,而是数据类型的零值(zero value),除非满足特定条件:
-
普通 static 变量:如
public static int count = 10;→ 准备阶段赋值为0(int 的零值),10要等到初始化阶段执行<clinit>方法时才真正赋值 -
static final 基本类型或字符串常量:如
public static final String NAME = "JVM";或public static final int MAX = 100;→ 编译期能确定的常量,会在准备阶段直接赋成代码值(因为编译器为其生成了ConstantValue属性) -
static final 引用类型(非常量表达式):如
public static final List<String> LIST = new ArrayList<>();→ 仍按零值处理(即null),实际初始化延迟到<clinit>阶段
这个阶段不做的事
准备阶段严格限定职责,以下操作均不会发生:
- 不会执行任何 Java 代码(包括静态代码块、构造器、赋值语句)
- 不会为实例变量分配内存(哪怕它们也在类里声明)
- 不会解析符号引用(那是解析阶段的任务)
- 不会触发类的初始化(初始化阶段才执行
<clinit>)
一个小验证例子
写一个类:
public class PrepTest {public static int a = 123;
public static final int b = 456;
public static final String c = "hello";
}
在准备阶段结束后:
– a == 0(零值)
– b == 456(ConstantValue 属性生效)
– c == "hello"(字符串字面量常量也走 ConstantValue)


















