运行时常量池,字符串常量池

本文介绍了Java中的常量池,包括class常量池,用于存放编译器生成的字面量和符号引用;运行时常量池,线程共享,可动态生成常量;还阐述了运行时常量池与Class文件常量池的区别,以及字符串常量池的特点,如全局共享,不同JDK版本位置有变化等。

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

class常量池

class常量池简介:
我们写的每一个Java类被编译后,就会形成一份class文件;class文件中除了包含类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池(constant pool table),用于存放编译器生成的各种字面量(Literal)和符号引用(Symbolic References);
每个class文件都有一个class常量池。

 

 

每个class文件都有一个常量池。反汇编可以看出。

运行时常量池

线程共享(在方法区中,永久区、元空间只是方法区的实现)

  Java语言并不要求常量一定只能在编译期产生,运行期间也可能产生新的常量,这些常量被放在运行时常量池中。

      Class 文件中的常量池(编译器生成的字面量和符号引用)会在类加载后被放入这个区域。

除了在编译期生成的常量,还允许动态生成,例如 String 类的 intern()。

  这里所说的常量包括:基本类型包装类(包装类不管理浮点型,整形只会管理-128到127)和String(也可以通过String.intern()方法可以强制将String放入常量池)

 

运行时常量池与Class文件常量池的区别

JVM对Class文件中每一部分的格式都有严格的要求,每一个字节用于存储那种数据都必须符合规范上的要求才会被虚拟机认可、装载和执行;但运行时常量池没有这些限制,除了保存Class文件中描述的符号引用,还会把翻译出来的直接引用也存储在运行时常量区
相较于Class文件常量池,运行时常量池更具动态性,在运行期间也可以将新的变量放入常量池中,而不是一定要在编译时确定的常量才能放入。最主要的运用便是String类的intern()方法
在方法区中,常量池有运行时常量池和Class文件常量池;但其中的内容是否完全不同,暂时还未得知
 

在JDK1.7之前运行时常量池逻辑包含字符串常量池存放在方法区, 此时hotspot虚拟机对方法区的实现为永久代

在JDK1.7 字符串常量池被从方法区拿到了堆中, 这里没有提到运行时常量池,也就是说字符串常量池被单独拿到堆,运行时常量池剩下的东西还在方法区, 也就是hotspot中的永久代

在JDK1.8 hotspot移除了永久代用元空间(Metaspace)取而代之, 这时候字符串常量池还在堆, 运行时常量池还在方法区, 只不过方法区的实现从永久代变成了元空间(Metaspace) 
 

字符串常量池

  HotSpot VM里,记录interned string的一个全局表叫做StringTable,它本质上就是个HashSet<String>。注意它只存储对java.lang.String实例的引用,而不存储String对象的内容

在JDK6.0中,StringTable的长度是固定的,长度就是1009,因此如果放入String Pool中的String非常多,就会造成hash冲突,导致链表过长,当调用String#intern()时会需要到链表上一个一个找,从而导致性能大幅度下降;
在JDK7.0中,StringTable的长度可以通过参数指定:
-XX:StringTableSize=66666
 

字符串常量池是全局共享的,jvm只存在一份。

 

元空间

慢慢完善吧

### Java 运行常量池字符串常量池的关系 运行常量池字符串常量池是 Java 中两个重要的概念,虽然它们之间存在一定的联系,但也有一些显著的区别。 #### 1. **运行常量池** 运行常量池是一个类或接口在 JVM 加载后的内存表示形式之一,它是方法区的一部分。当 JVM 将字节码文件加载到内存中,会将其中的静态常量池内容复制到运行常量池中[^3]。 运行常量池不仅包含了编译期生成的字面量和符号引用,还可以通过某些机制(如 `String.intern()` 方法)动态地向其中添加新条目[^4]。因此,运行常量池具有更大的灵活性和动态性。 #### 2. **字符串常量池** 字符串常量池运行常量池的一个子集,专门用于存储字符串类型的常量。它的主要作用是为了提高性能并节省内存空间——如果多个相同的字符串对象被创建,则只会保存一份实际的内容,并让这些引用指向同一个地址。 例如,在以下代码片段中: ```java String s1 = "hello"; String s2 = "hello"; System.out.println(s1 == s2); // 输出 true ``` `s1` 和 `s2` 的值相同,因为 `"hello"` 已经存在于字符串常量池中,所以不会再次创建新的实例[^1]。 #### 3. **两者关系** - 字符串常量池可以看作是运行常量池的一部分,但它专注于处理字符串类型的常量。 - 所有字符串字面量都会自动进入字符串常量池,而其他类型的常量则存放在更广泛的运行常量池中[^2]。 - 使用 `String.intern()` 方法可以让任何字符串对象尝试加入字符串常量池。如果该字符串已经存在于池中,则返回其引用;否则将其添加进去后再返回引用。 总结来说,运行常量池涵盖了整个程序执行过程中的各种常量数据结构,其中包括但不限于字符串常量池这一特定区域。 --- ### 示例代码展示 下面是一段演示如何利用 `intern()` 方法操作字符串常量池的例子: ```java public class StringPoolExample { public static void main(String[] args) { String str1 = new StringBuilder("Java").append("World").toString(); System.out.println(str1.intern() == str1); // 可能输出 false 或 true, 视具体实现而定 String str2 = "JavaWorld"; System.out.println(str1 == str2); // 输出 false (不同对象) System.out.println(str1.intern() == str2); // 输出 true (经过 intern 后一致) } } ``` 上述例子展示了手动调用 `intern()` 对象的行为及其影响。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值