跳到正文
Joeplover
学习笔记·2026-07-12·约 3 分钟阅读

一步步问清 JVM 方法区、元空间、常量池、字符串常量池和 static 引用

从 x、S、"hello" 和局部变量 a 出发,一步步厘清 JVM 方法区、元空间、运行时常量池、字符串常量池与 static 引用的归属。

代码编辑器与计算机芯片,象征 JVM 内存与运行时机制

一步步问清 JVM 方法区、元空间、常量池、字符串常量池和 static 引用

很多人学 JVM 的方法区时,最容易卡住的不是“方法区是什么”,而是“一个变量、一个字符串、一个引用到底分别放哪”。我这次就按最小问题一步步问清楚,最后把它们拼成一张完整的内存图。

一、方法区里到底存什么?

先看这个例子:

class A {
    static final String S = "hello";
    int x = 1;
    void f() {}
}

这里的方法区/元空间,主要放的是“类的信息”:

  • 类名 A
  • 字段 x 的信息
  • 方法 f 的信息
  • 静态字段 S 的信息
  • 运行时常量池

它不是拿来放 new 出来的对象的。对象本体不在这里。

二、x 存哪,S 存哪?

x 是实例字段,它属于对象,所以 new A() 之后,x = 1 存在堆里的那个对象里。

S 是 static final 字段,它属于类,不属于某个对象,所以它跟着类走,不跟着对象走。

所以这两个东西的归属不同:

  • x:对象字段,跟对象走
  • S:类字段,跟类走

三、常量池和字符串常量池不是一回事

这是最容易混的地方。

常量池可以理解成 .class 里的“表格”和“目录”,里面记着:

  • 类名
  • 字段名
  • 方法名
  • 字符串字面量
  • 各种符号引用

它更像“索引”,不是对象本体。

字符串常量池只管字符串对象。比如 "hello" 这种字符串,JVM 会尽量复用:

  • 如果池里已经有 "hello",就直接用
  • 如果没有,就创建一个,再放进去

所以:

  • 常量池:记名字、记引用线索
  • 字符串常量池:管理字符串对象复用

四、static final String S = "hello" 到底怎么走

这行代码可以拆成三层看:

  1. S 这个字段属于类,跟类一起加载到方法区/元空间这一侧。
  2. "hello" 这个字符串对象,通常在堆里。
  3. 字符串常量池负责判断这个字符串能不能复用。

所以更准确地说:

  • S 是类级别的静态字段
  • S 指向堆里的字符串对象
  • "hello" 会受字符串常量池管理

五、static 修饰的引用数据类型变量,它的引用存哪?

这是最后要说清楚的一点。

如果是 static 修饰的引用类型变量,那么它的“引用值”不在栈里,而是在类这一侧,和静态字段一起管理。 它指向的对象,才在堆里。

所以:

  • static 引用:类侧
  • 引用指向的对象:堆

六、局部变量引用和 static 引用不一样

如果是普通方法里的局部变量引用,比如:

void test() {
    A a = new A();
}

这里的 a 这个引用变量是在栈里的。

所以引用要分两种:

  • 局部变量引用:栈里
  • static 引用:类侧

七、一张总图

方法区 / 元空间
┌────────────────────────────┐
│ 类信息 A                    │
│ - 类名 A                    │
│ - 字段 x                    │
│ - 方法 f                    │
│ - static 字段 S             │
│ - 运行时常量池               │
└────────────────────────────┘

堆
┌────────────────────────────┐
│ A 对象                      │
│ - x = 1                     │
│                            │
│ 字符串对象 "hello"          │
└────────────────────────────┘

栈
┌────────────────────────────┐
│ 局部变量 a                  │
│ a 指向堆里的 A 对象          │
└────────────────────────────┘

八、最后一句话总结

x 跟对象走,S 跟类走;字符串对象在堆里;常量池是目录,字符串常量池是字符串对象的复用池;static 引用属于类侧,不在栈里。