跳转到内容
主菜单
主菜单
移至侧栏
隐藏
导航
首页
最近更改
随机页面
MediaWiki帮助
代码酷
搜索
搜索
中文(中国大陆)
外观
创建账号
登录
个人工具
创建账号
登录
未登录编辑者的页面
了解详情
贡献
讨论
编辑“︁
常量池
”︁
页面
讨论
大陆简体
阅读
编辑
编辑源代码
查看历史
工具
工具
移至侧栏
隐藏
操作
阅读
编辑
编辑源代码
查看历史
常规
链入页面
相关更改
特殊页面
页面信息
外观
移至侧栏
隐藏
您的更改会在有权核准的用户核准后向读者展示。
警告:
您没有登录。如果您进行任何编辑,您的IP地址会公开展示。如果您
登录
或
创建账号
,您的编辑会以您的用户名署名,此外还有其他益处。
反垃圾检查。
不要
加入这个!
{{Note|本条目讲解的是'''JVM中的常量池''',与编程语言中的常量池(如Java的`String`常量池)有部分重叠但属于不同维度的概念。}} == 概述 == '''常量池(Constant Pool)'''是JVM方法区(Method Area)的一部分,用于存储编译期生成的各种字面量(Literal)和符号引用(Symbolic Reference)。它是[[Java虚拟机规范]]中定义的核心数据结构之一,直接影响类加载、内存分配和动态链接的过程。 === 核心特性 === * '''编译期生成''':由`javac`编译器在编译`.java`文件时创建 * '''静态存储''':内容在类加载后存入方法区 * '''共享访问''':同一类的多个实例共享同一份常量池 * '''动态链接''':运行期间将符号引用解析为直接引用 == 常量池结构 == JVM常量池采用类似数组的索引结构,每个条目通过`#n`(如`#7`)的方式引用。主要包含以下类型: {| class="wikitable" |+ 常量池主要条目类型 ! 类型 !! 示例 !! 说明 |- | `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编码字符串 |} === 内存模型 === <mermaid> 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 </mermaid> == 代码示例 == 以下Java代码演示常量池的实际使用: <syntaxhighlight lang="java"> public class ConstantPoolDemo { public static void main(String[] args) { String s1 = "JVM"; String s2 = "JVM"; System.out.println(s1 == s2); // true } } </syntaxhighlight> === 字节码分析 === 通过`javap -v`命令查看编译后的常量池(节选): <syntaxhighlight lang="text"> Constant pool: #1 = Methodref #4.#20 // java/lang/Object."<init>":()V #2 = String #21 // JVM ... #21 = Utf8 JVM </syntaxhighlight> 输出结果为`true`,因为: 1. 编译器将字面量`"JVM"`存入常量池(`#2`) 2. `s1`和`s2`都指向常量池的同一引用 == 运行期行为 == JVM处理常量池的关键阶段: === 类加载阶段 === 1. 验证常量池的格式合规性 2. 准备阶段分配内存空间 3. 解析阶段将符号引用转为直接引用 === 符号引用解析 === 解析过程遵循以下公式: <math> resolve(ref) = \begin{cases} direct\_address & \text{已解析}\\ lookup\_and\_bind(ref) & \text{未解析} \end{cases} </math> == 实际应用案例 == === 字符串驻留(String Interning) === Java字符串常量池实际是运行时常量池的子集: <syntaxhighlight lang="java"> String s1 = new String("Hello").intern(); String s2 = "Hello"; System.out.println(s1 == s2); // true </syntaxhighlight> === 动态代理生成 === `java.lang.reflect.Proxy`在运行时生成代理类时,会动态构建常量池: <syntaxhighlight lang="java"> Proxy.newProxyInstance( loader, new Class[]{Runnable.class}, (proxy, method, args) -> null ); </syntaxhighlight> == 性能影响 == * '''优点''': * 减少重复字面量的内存占用 * 加速符号引用解析 * '''缺点''': * 过大的常量池会增加方法区内存压力 * 可能引发`OutOfMemoryError: Metaspace` == 常见问题 == {{Q&A |question = 常量池会存储基本类型的包装对象吗? |answer = 不会。常量池仅存储编译期可知的字面量,如`Integer 100`(对应`CONSTANT_Integer`),但不会存储`new Integer(100)`这样的对象引用。 }} {{Q&A |question = 如何查看完整的常量池内容? |answer = 使用`javap -v ClassName`命令,或通过ASM、ByteBuddy等字节码工具库编程访问。 }} == 进阶知识 == === 多版本常量池 === 从Java 7开始,字符串常量池被移至堆内存,而其他常量池内容仍保留在方法区(元空间)。 === 动态常量池 === 通过`java.lang.invoke`包可以在运行时动态修改常量池(需开启`-XX:+UnlockExperimentalVMOptions`)。 == 参见 == * [[Java虚拟机规范]]第4章 * [[类文件结构]] * [[方法区]] [[Category:JVM]] [[Category:Java内存模型]] [[Category:计算机科学]] [[Category:面试技巧]] [[Category:JVM相关]]
摘要:
请注意,所有对代码酷的贡献均被视为依照知识共享署名-非商业性使用-相同方式共享发表(详情请见
代码酷:著作权
)。如果您不希望您的文字作品被随意编辑和分发传播,请不要在此提交。
您同时也向我们承诺,您提交的内容为您自己所创作,或是复制自公共领域或类似自由来源。
未经许可,请勿提交受著作权保护的作品!
取消
编辑帮助
(在新窗口中打开)
该页面使用的模板:
模板:Note
(
编辑
)
模板:Q&A
(
编辑
)