运行时常量池是什么

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

前言:记录一下常量池。

原文:https://blog.youkuaiyun.com/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中的字符串(String Pool,也称为字符串常量池)和运行时常(Runtime Constant Pool)的定义。根据用户提供的引用,我们需要清晰地定义这两个概念,并说明它们的区别和联系。 首先,根据引用[1]: - 运行时常和字符串常量池在JDK7之前都在方法区;JDK7及之后,运行时常仍在方法区,而字符串常量池被移动到堆内存中。 - 字符串常量池专门处理字符串字面量的存储和重用,是运行时常的一个特殊功能区域。 引用[2]提到: - 字符串常量池运行时常都与常量数据的存储和管理有关。 - 涉及字面量、引用、符号引用和直接引用的概念。 引用[3]详细说明了: - 运行时常是每个类都有的,它在类加载后存放class文件常量池(即静态常量池)的内容。 - 运行时常在方法区(JDK8及之后是元空间)中,而字符串常量池在堆中(从JDK7开始)。 - 运行时常包含的内容:类信息、属性信息、方法信息以及其他基础类型的常量池(如int, long等),而字符串常量池只存放字符串。 引用[4]强调: - 运行时常在JDK8位于方法区(元空间)。 引用[5]指出: - 运行时常是方法区的一部分,存放编译期生成的各种字面量和符号引用。 综合以上引用,我们可以给出定义: 1. **字符串(String Pool)**: - 也称为字符串常量池,是JVM中专门用于存储字符串字面量(如直接写的双引号字符串)和字符串对象的特殊区域。 - 主要功能是重用字符串,避免创建重复的字符串对象,从而节省内存。 - 位置变化: - JDK6及之前:方法区(永久代) - JDK7及之后:堆内存 - 它只存储字符串对象,并且是全局共享的。 2. **运行时常(Runtime Constant Pool)**: - 是每个类或接口在运行常量池表示,它是在类加载后,从class文件的常量池(静态常量池)中构建出来的。 - 存储的内容包括: - 字面量:如文本字符串(但字符串字面量实际存储在字符串中,运行时常中存储的是对字符串中对象的引用)、final常量值、基本类型值等。 - 符号引用:类和接口的全限定名、字段的名称和描述符、方法的名称和描述符。 - 在类加载过程的解析阶段,符号引用会被解析为直接引用(如内存地址)。 - 位置:始终在方法区中,具体实现: - JDK7及之前:永久代(作为方法区的实现) - JDK8及之后:元空间(作为方法区的实现) 此外,注意两者关系: - 运行时常包含对字符串常量池的引用。也就是说,当一个类中有一个字符串字面量,该字面量会存储在字符串常量池中,而运行时常中存储的是指向字符串中该字符串对象的引用。 下面用示例说明: ```java public class Example { public static void main(String[] args) { String s1 = "hello"; // 字面量"hello"会进入字符串常量池 String s2 = new String("world"); // 也会使用字符串常量池中的"world" } } ``` - 当加载Example类,其class文件中的常量池(静态常量池)被加载到运行时常(在方法区)中。 - 字符串字面量"hello"和"world"会被放入字符串常量池(在堆中),运行时常中存储的是指向这两个字符串对象的引用。 总结: - 字符串:存储字符串对象,位于堆(JDK7+),全局共享。 - 运行时常:每个类都有一个,存储该类的常量信息(包括符号引用和字面量引用),位于方法区(元空间)。 最后,根据要求,生成相关问题。 </think>### 字符串运行时常定义解析 #### 1. **字符串(String Pool)** 字符串JVM中**专门存储字符串字面量**的全局共享内存区域[^1][^3],核心特性如下: ```mermaid graph LR A[字符串] --> B[存储内容] B --> C1(字符串字面量<br>e.g. String s = “abc”) B --> C2(intern()方法缓存的字符串) A --> D[位置演变] D --> E1(JDK6-:方法区) D --> E2(JDK7+:堆内存) A --> F[功能] F --> G1(避免重复创建相同字符串) F --> G2(通过==实现字符串复用) ``` > **示例**: > `String s1 = "hello";` // 首次创建,存入字符串 > `String s2 = "hello";` // 直接引用中对象 > `System.out.println(s1 == s2); // true` #### 2. **运行时常(Runtime Constant Pool)** 运行时常是**每个类独有的内存区域**,在类加载创建[^3][^5],核心定义为: ```mermaid graph TD R[运行时常] --> S[数据来源] S --> T1(类文件常量池<br>编译期生成的符号引用) S --> T2(动态添加数据<br>如String.intern()) R --> U[存储内容] U --> V1(类/方法/字段符号引用) U --> V2(基本类型常量<br>int,float等) U --> V3(字符串常量引用<br>指向字符串) R --> W[位置] W --> X(始终在方法区)<br> W --> Y(JDK8+:元空间) ``` > **关键机制**: > 类加载JVM将class文件中常量池(静态常量池)的内容加载到运行时常[^4][^5],并在解析阶段: > 1. 将符号引用(如`java/lang/Object`)解析为直接引用(内存地址) > 2. 确保字符串引用指向字符串的实际对象[^1][^3] #### 3. 二者核心区别 | 特性 | 字符串 | 运行时常 | |--------------------|----------------------------|--------------------------| | **存储内容** | 仅字符串对象 | 符号引用+基本类型常量+字符串引用 | | **作用域** | 全局共享 | 每个类独立 | | **主要功能** | 字符串重用 | 类元数据存储与动态扩展 | | **内存位置** | JDK7+在堆 | 始终在方法区(元空间) | | **是否允许动态添加** | 是(通过intern()) | 是 | > **交互示例**: > 当执行`String s = new String("abc")`: > 1. 字面量"abc"存入**字符串**(若不存在) > 2. 类常量池生成"abc"符号引用 > 3. 类加载符号引用存入该类的**运行时常** > 4. 解析阶段将符号引用转为指向字符串的直接引用[^2][^3]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值