运行时常量池是什么

本文详细解析Java运行时常量池的概念、作用及其实现原理。运行时常量池是方法区的一部分,用于存储编译期生成的字面量和符号引用。Java允许在运行期生成新的常量,如基本类型包装类和String,这些常量同样存储在运行时常量池中。文章通过示例代码展示了常量池的动态特性和存储机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:记录一下常量池。

原文:https://blog.csdn.net/ooppookid/article/details/51619551

1、运行时常量池是什么

运行时常量池(Runtime Constant Pool),它是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述等信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到常量池中。

了解java内存管理看这里:jvm是如何管理内存的
如图:
这里写图片描述

运行时常量是相对于常量来说的,它具备一个重要特征是:动态性。当然,值相同的动态常量与我们通常说的常量只是来源不同,但是都是储存在池内同一块内存区域。

Java语言并不要求常量一定只能在编译期产生,运行期间也可能产生新的常量,这些常量被放在运行时常量池中。这里所说的常量包括:基本类型包装类(包装类不管理浮点型,整形只会管理-128到127)和String(也可以通过String.intern()方法可以强制将String放入常量池)。

了解基本类型包装器看这里:什么是基本类型包装器
例子:

public class TestConst {

    public static String CONST_A = "the const b";// 编译时放入常量池

    public String const_b;

    public Integer const_b_i;

    public Integer const_b_ii;

    public Float const_b_f;

    public static void main(String[] args) {
        TestConst testConst = new TestConst();
        testConst.const_b = "the const b";// 运行时放入常量池
        testConst.const_b_i = 12;// 运行时放入常量池

        testConst.const_b_ii = 128;// 超过127,所以不会放入常量池
        testConst.const_b_f = 2.0f;// 浮点包装器不放入常量池

        String const_c = "the const b";// 运行时放入常量池
        Integer const_c_i = 12;// 运行时放入常量池

        Integer const_c_ii = 128;// 超过127,所以不会放入常量池
        Float const_c_f = 2.0f;// 浮点包装器不放入常量池

        System.out.println(CONST_A == const_c);
        System.out.println(CONST_A == testConst.const_b);
        System.out.println(testConst.const_b == const_c);
        System.out.println(testConst.const_b_i == const_c_i);
        System.out.println(testConst.const_b_ii == const_c_ii);
        System.out.println(testConst.const_b_f == const_c_f);
    }
}

运行结果:

true
true
true
true
false
false

Java虚拟机对Class文件的每一部分的格式都有严格的规定,每一个字节用于存储哪种数据都必须符合规范,这样才会被虚拟机装载和执行。但对于运行时常量池,Java虚拟机规范没有做任何细节的要求,不同的提供商实现的虚拟机可以按照自己的需要来实现这个内存区域。

由于运行时常量池是方法区的一部分,所以会受到方法区内存的限制,当常量池无法再申请到内存时会抛出OutOfMemoryError: PermGen space异常。

在Java 8以后移除了方法区,由本地元空间代替,运行时常量池也放在了本地元空间中,如果这个区内存溢出,则会抛出OutOfMemoryError: Metaspace错误。

<think>好的,我现在需要回答用户关于常量池运行时常的关系以及它们存放位置的问题。首先,我得确保自己正确理解这两个概念,然后以简单易懂的方式解释给用户。 用户之前问过线程和栈的关系,现在转向常量池,可能是对JVM内存结构感兴趣。所以需要明确常量池运行时常JVM中的位置和作用。 首先,常量池(Constant Pool)通常指的是Class文件中的常量池,这部分是静态的,存储在磁盘上的.class文件中。而运行时常(Runtime Constant Pool)属于方法区的一部分,在JVM运行内存中存在。当类被加载,Class文件中的常量池会被加载到内存中,成为运行时常。 需要区分不同版本JDK中的变化,比如JDK 8之前方法区由永久代实现,之后被元空间取代,但运行时常仍属于方法区。而字符串常量池在堆中,这点可能需要提到,但用户的问题可能更关注Class常量池运行时常的关系。 可能需要用比喻来帮助理解。比如,Class常量池像菜谱的材料清单,运行时常是实际厨房里的材料摆放位置。当JVM加载类,把菜谱中的材料清单搬到厨房里,变成运行可用的东西。 要注意用户可能的疑问点,比如两者的存储位置,是否在同一个区域,以及它们之间的转换过程。同要说明运行时常的动态性,例如可以在运行添加新的常量,如String.intern()的例子。 需要检查是否有混淆概念的地方,比如字符串常量池运行时常的区别,确保回答准确。另外,要确认常量池在.class文件中的结构,以及在JVM内存中的位置,确保信息正确。 最后,用简洁的总结来强调关键点,帮助用户记忆。可能需要分点说明,结构清晰,避免冗长。可能还要提到相关异常,如OutOfMemoryError,但用户的问题可能不涉及这部分,所以简要带过即可。 总结思考步骤: 1. 确定常量池运行时常的定义。 2. 解释它们的关系:静态与动态,加载过程。 3. 明确存储位置:Class文件 vs 方法区(元空间)。 4. 使用比喻帮助理解。 5. 举例说明运行时常的动态性。 6. 区分其他常量池,如字符串常量池。 7. 结构清晰,语言通俗,避免技术术语过多。</think>### **用「菜谱和厨房」的比喻秒懂常量池关系**: --- #### **1. 常量池(Constant Pool)——静态菜谱清单** - **是什么**:`.class`文件中的**静态数据字典**(比如类名、方法名、字符串字面量、数字常量等)。 - **存在哪**:**躺在硬盘里**(类未加载,它就是.class文件里的一串二进制数据)。 - **举个栗子**: ```java String s = "Java"; // "Java"这个字符串字面量会写入.class的常量池 ``` --- #### **2. 运行时常(Runtime Constant Pool)——动态厨房材料区** - **是什么**:类加载后,JVM**把常量池“搬”到内存里**的升级版(属于方法区的一部分)。 - **存在哪**:**在JVM内存的方法区**(JDK8前叫永久代,JDK8+叫元空间,但逻辑上仍属于方法区)。 - **关键特性**: - **动态性**:运行可以往里面添加新常量(比如`String.intern()`方法动态添加字符串)。 - **全局性**:被所有线程共享。 --- ### **3. 二者关系:菜谱 → 厨房** - **步骤1**:你写好的`Test.java`编译成`Test.class`,此时常已确定(类似写好菜谱)。 - **步骤2**:JVM加载`Test.class`,**将.class里的常量池“搬”到内存**,变成运行时常(类似照着菜谱把材料搬进厨房)。 - **步骤3**:运行若用到新常量(比如动态生成的字符串),可能被加入运行时常(类似临加调料到厨房)。 --- ### **4. 内存位置总结** | **类别** | **存储位置** | **是否可变** | |-------------------|--------------------------------|------------------| | 常量池 | `.class`文件(硬盘) | 编译后固定不变 | | 运行时常 | 方法区(JDK8+在元空间,使用本地内存) | 运行动态添加内容 | --- ### **5. 对比其他“”别搞混!** - **字符串常量池(String Table)**: - **存在堆内存**(JDK7开始从永久代移出)。 - 专门存字符串对象的**唯一引用**(`String.intern()`的核心作用区)。 - **包装类常量池(如IntegerCache)**: - 存在堆中,但范围有限(比如Integer默认缓存-128~127)。 --- ### **6. 直接看代码理解动态性** ```java String s1 = new StringBuilder("Ja").append("va").toString(); System.out.println(s1.intern() == s1); // JDK6 false,JDK7+ true // intern()方法:若字符串不在运行时常,则添加并返回引用; // 若已存在,直接返回已有引用。不同JDK版本行为可能有差异! ``` --- ### **7. 一句话总结** - **常量池是死的(硬盘数据),运行时常是活的(内存运行数据)**。 - JVM加载类,“复活”常量池,并赋予它动态扩展的能力!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值