当前位置: 首页 > article >正文

JVM中的运行时常量池详解

        运行时常量池(Runtime Constant Pool)是每一个类或接口的常量池(Constant_Pool)的运行时表示形式,它包括了若干种不同的常量:从编译期可知的数值字面量到必须运行期解析后才能获得的方法或字段引用。运行时常量池扮演了类似传统语言中符号表(Symbol Table)的角色,不过它存储数据范围比通常意义上的符号表要更为广泛。

        每一个运行时常量池都分配在Java虚拟机的方法区之中,在类和接口被加载到虚拟机后,对应的运行时常量池就被创建出来。

         在创建类和接口的运行时常量池时,可能会发生如下异常情况:

 当创建类或接口的时候,如果构造运行时常量池所需要的内存空间超过了方法区所能提供的最 大值,那Java虚拟机将会抛出一个OutOfMemoryError异常。

        上面的表述实在有些晦涩,不易初学者理解,所以下面我通过一个详细的例子,用通俗的语言一步步带你理解“运行时常量池”是怎么工作的。


假设我们写了一段简单的 Java 代码:

public class Example {
    public static void main(String[] args) {
        int number = 42;
        String message = "Hello, World!";
        System.out.println(message);
    }
}

第一步:代码编译成 .class 文件

        当你用 javac Example.java 编译这段代码时,Java 编译器会生成一个 Example.class 文件。这个文件里有一个“常量池”(Constant Pool),它就像一个清单列表,把代码里用到的“固定信息”列出来。这些信息在编译时就已经确定了,但它们只是“符号”或者“名字”,还没有变成程序运行时能直接用的东西。

在这个例子中,常量池里可能会记录这些内容:

  1. 数字 42(一个整数字面量)。
  2. 字符串 "Hello, World!"(一个字符串字面量)。
  3. 方法引用 System.out.println(表示我们要调用的那个打印方法)。
  4. 一些类名和字段名,比如 java/lang/System 和 out

这些信息被存成一种特殊的格式,比如:

  • #1 = Integer 42(表示数字 42)。
  • #2 = String "Hello, World!"(表示字符串)。
  • #3 = Methodref java/lang/System.out.println(表示打印方法)。

        这些只是符号,不是最终的内存地址或实际数据。常量池就像一个“备忘录”,记录了代码里用到的所有关键东西。

第二步:JVM 加载并创建运行时常量池

        当你运行 java Example 时,JVM 会加载 Example.class 文件。它会把文件里的常量池拿出来,变成一个“活的”东西——这就是“运行时常量池”(Runtime Constant Pool)。这个运行时常量池存在于 JVM 的内存中(具体在方法区里),它的任务是把刚才那些符号“翻译”成程序能用的实际内容。

比如:

  • 数字 42:在运行时常量池里,它还是 42,但会被关联到程序的计算中,直接用在 int number = 42 赋值。
  • 字符串 "Hello, World!":JVM 会检查字符串池(String Pool,专门用来存字符串),如果池子里已经有 "Hello, World!",就直接用那个;如果没有,就创建一个新的字符串对象,然后把它的引用存到运行时常量池里。
  • 方法 System.out.println:运行时常量池会去查找 System 类的具体位置,找到 out 这个字段(它是一个 PrintStream 对象),再找到 println 方法的实际内存地址。这样,程序就知道去哪里执行打印操作。

第三步:程序运行时的翻译过程

现在程序开始执行 main 方法:

  1. int number = 42;

    • JVM 直接从运行时常量池拿到 42,把它赋值给变量 number。这里没什么复杂的,因为 42 是个简单的数字。
  2. String message = "Hello, World!";

    • JVM 从运行时常量池里找到 "Hello, World!" 的引用。因为它是字符串字面量,JVM 会确保它在字符串池里只存在一份,然后让 message 指向这个字符串。
  3. System.out.println(message);

    • JVM 去运行时常量池查找 System.out.println 的符号引用。
    • 它先找到 System 类(可能在内存地址比如 0x1234),然后找到 out 字段(一个 PrintStream 对象,比如在 0x5678),再找到 println 方法的具体地址(比如 0x9abc)。
    • 最后,JVM 调用这个方法,把 message 的内容 "Hello, World!" 打印出来。

比喻解释:运行时常量池像一个“翻译官”

想象你去一个陌生的国家旅游,带了一本旅游手册,里面写着:

  • “酒店” 在第 1 页。
  • “问候语:你好” 在第 2 页。
  • “找餐厅的方法” 在第 3 页。

        这本手册就像编译时的“常量池”,它只是记录了信息,但你还不知道具体怎么用。到了当地,你请了个导游(运行时常量池),他拿着手册帮你翻译:

  • “酒店” → 带你去具体的酒店地址。
  • “你好” → 教你怎么发音跟当地人打招呼。
  • “找餐厅的方法” → 告诉你具体的路线。

        运行时常量池就干这个活儿:把代码里的符号(手册上的词)翻译成实际能用的东西(地址、数据),让程序顺利跑起来。

总结

通过这个例子,可知运行时常量池的核心作用是:

  • 保存编译时确定的常量(数字、字符串、方法引用等)。
  • 在程序运行时,把这些符号解析成实际的内存地址或数据。
  • 确保代码里的每一部分都能找到它需要的东西,正确执行。

http://www.kler.cn/a/600257.html

相关文章:

  • 【QA】Qt有哪些迭代器模式的应用?
  • Vue中的状态管理器Vuex被Pinia所替代-上手使用指南
  • 买卖股票的最佳时机(js实现,LeetCode:121)
  • 【Excel使用技巧】某列保留固定字段或内容
  • 多语言语料库万卷·丝路2.0开源,数据模态全面升级,搭建文化交流互鉴AI桥梁
  • 原子化 CSS
  • 护网面试题总结
  • Java 集合 List、Set、Map 区别与应用
  • 基于Spring Boot + Vue的银行管理系统设计与实现
  • XDP/eBPF来包过滤-已上机验证
  • CSS实现当鼠标悬停在一个元素上时,另一个元素的样式发生变化的效果
  • 《AI Agent智能应用从0到1定制开发》学习笔记:使用RAG技术增强大模型能力,实现与各种文档的对话
  • CSS语言的双向链表
  • 网络运维学习笔记(DeepSeek优化版) 020 HCIA-Datacom新增知识点02 SDN与NFV概述
  • 6(六)Jmeter线程数分段加压
  • 基于Linux的多进程并发服务器设计与实现
  • RISC-V AIA学习2---IMSIC
  • docker pull时报错:https://registry-1.docker.io/v2/
  • tortoiseSVN、source insignt、J-flash使用
  • 【Hbase】列族版本问题