跳转到内容

常量池

来自代码酷
Admin留言 | 贡献2025年5月12日 (一) 00:26的版本 (Page creation by admin bot)

(差异) ←上一版本 | 已核准修订 (差异) | 最后版本 (差异) | 下一版本→ (差异)

模板:Note

概述[编辑 | 编辑源代码]

常量池(Constant Pool)是JVM方法区(Method Area)的一部分,用于存储编译期生成的各种字面量(Literal)和符号引用(Symbolic Reference)。它是Java虚拟机规范中定义的核心数据结构之一,直接影响类加载、内存分配和动态链接的过程。

核心特性[编辑 | 编辑源代码]

  • 编译期生成:由`javac`编译器在编译`.java`文件时创建
  • 静态存储:内容在类加载后存入方法区
  • 共享访问:同一类的多个实例共享同一份常量池
  • 动态链接:运行期间将符号引用解析为直接引用

常量池结构[编辑 | 编辑源代码]

JVM常量池采用类似数组的索引结构,每个条目通过`#n`(如`#7`)的方式引用。主要包含以下类型:

常量池主要条目类型
类型 示例 说明
`CONSTANT_Class` `#7 = Class #8` 类/接口的符号引用
`CONSTANT_Fieldref` `#9 = Fieldref #10.#11` 字段的符号引用
`CONSTANT_Methodref` `#12 = Methodref #13.#14` 方法的符号引用
`CONSTANT_String` `#15 = String #16` 字符串字面量
`CONSTANT_Integer` `#17 = Integer 123` 整型字面量
`CONSTANT_Utf8` `#18 = Utf8 "example"` UTF-8编码字符串

内存模型[编辑 | 编辑源代码]

classDiagram class ClassFile { +magic: u4 +constant_pool_count: u2 +constant_pool: cp_info[] +... } class cp_info { +tag: u1 +info: u1[] } ClassFile "1" *-- "1..*" cp_info

代码示例[编辑 | 编辑源代码]

以下Java代码演示常量池的实际使用:

public class ConstantPoolDemo {
    public static void main(String[] args) {
        String s1 = "JVM";
        String s2 = "JVM";
        System.out.println(s1 == s2);  // true
    }
}

字节码分析[编辑 | 编辑源代码]

通过`javap -v`命令查看编译后的常量池(节选):

Constant pool:
   #1 = Methodref          #4.#20         // java/lang/Object."<init>":()V
   #2 = String             #21            // JVM
   ...
   #21 = Utf8               JVM

输出结果为`true`,因为: 1. 编译器将字面量`"JVM"`存入常量池(`#2`) 2. `s1`和`s2`都指向常量池的同一引用

运行期行为[编辑 | 编辑源代码]

JVM处理常量池的关键阶段:

类加载阶段[编辑 | 编辑源代码]

1. 验证常量池的格式合规性 2. 准备阶段分配内存空间 3. 解析阶段将符号引用转为直接引用

符号引用解析[编辑 | 编辑源代码]

解析过程遵循以下公式: resolve(ref)={direct_address已解析lookup_and_bind(ref)未解析

实际应用案例[编辑 | 编辑源代码]

字符串驻留(String Interning)[编辑 | 编辑源代码]

Java字符串常量池实际是运行时常量池的子集:

String s1 = new String("Hello").intern();
String s2 = "Hello";
System.out.println(s1 == s2);  // true

动态代理生成[编辑 | 编辑源代码]

`java.lang.reflect.Proxy`在运行时生成代理类时,会动态构建常量池:

Proxy.newProxyInstance(
    loader, 
    new Class[]{Runnable.class}, 
    (proxy, method, args) -> null
);

性能影响[编辑 | 编辑源代码]

  • 优点
 * 减少重复字面量的内存占用
 * 加速符号引用解析
  • 缺点
 * 过大的常量池会增加方法区内存压力
 * 可能引发`OutOfMemoryError: Metaspace`

常见问题[编辑 | 编辑源代码]

模板:Q&A

模板:Q&A

进阶知识[编辑 | 编辑源代码]

多版本常量池[编辑 | 编辑源代码]

从Java 7开始,字符串常量池被移至堆内存,而其他常量池内容仍保留在方法区(元空间)。

动态常量池[编辑 | 编辑源代码]

通过`java.lang.invoke`包可以在运行时动态修改常量池(需开启`-XX:+UnlockExperimentalVMOptions`)。

参见[编辑 | 编辑源代码]