字符串池与常量池

24

让我们看一下以下代码片段:

  String s1 = "Hello";
  String s2 = "Hello"; 

由于内部化,两个变量都指向同一个对象。由于字符串是不可变的,只创建一个对象,两个变量都指向同一对象。

常量池也是一个东西,它保存在类中声明的所有常量(整数、字符串等)。它针对每个类是特定的。

 System.out.println("Hello");  // I believe this Hello is different from above.

问题:

  1. string pool 是指常量池中的一个常量字符串对象池吗?
  2. 如果是,String pool 在整个应用程序中是否通用或者特定于某个类?

https://dev59.com/elXTa4cB1Zd3GeqP3qQj?rq=1 - Engineer2021
可能是 在字符串常量上什么时候应该使用String的intern方法 的重复问题。 - Deduplicator
3个回答

22
我的问题是,
1. 字符串池是否指常量池中的常量字符串对象池?
不是。
“常量池”是类文件中特殊格式的字节集合,对Java类加载器具有意义。其中的“字符串”是序列化的,它们不是Java对象。它还包含许多种常量,而不仅仅是字符串。
请参见第4.4章 常量池表 Java虚拟机指令不依赖于类、接口、类实例或数组的运行时布局。相反,指令引用constant_pool表中的符号信息。
相比之下,“字符串池”在运行时使用(不仅仅是在类加载期间),仅包含字符串,“字符串池”中的字符串是Java对象。 “字符串池”是一个线程安全的弱映射,从java.lang.String实例到java.lang.String实例,用于对字符串进行内部化。 第3.10.5节字符串字面量说:

字符串字面量是对String类的实例的引用(§4.3.1,§4.3.3)。

此外,字符串字面量始终引用相同的String类实例。这是因为字符串字面量-更普遍地说,是常量表达式的值(§15.28)-被“内部化”以共享唯一实例,使用方法 String.intern 。


1
这个字符串池存储在哪里?正如链接http://java-performance.info/string-intern-in-java-6-7-8/所述,内部化的字符串存储在堆中。那么它是与其他对象所在的堆相同还是不同的?如果有任何Java文档或Java规范的链接,将不胜感激。 - nanosoft
3
有了分代垃圾收集器,新对象会在nursery中分配,但常驻字符串通常存在时间较长,因此实现可以跳过类加载器分配的字符串的nursery。JVM是否这样做是实现决策,并不是由Java虚拟机规范所要求的。Java语言规范是Java规范的另一半,但在内存管理问题上大多委托给JVM规范。 - Mike Samuel
1
你确定字符串池是一个弱映射吗?JVM规范中没有任何信息可以证明这一点。你是怎么发现的?你能提供一些参考资料,比如openjdk源代码或其他官方参考资料来证明这一点吗? - Eugene Maysyuk
@EugeneMaysyuk 如果我没记错的话,弱语义并没有被规范,但已经成为事实标准。请参见http://hg.openjdk.java.net/jdk9/jdk9/hotspot/file/910e24afc502/src/share/vm/classfile/stringTable.cpp#l193 - Mike Samuel

3
只有一个字符串池,所有字符串字面量都会自动进行内部化。
此外,还有其他用于自动装箱等的池。
常量池是将这些字面量放置在类中的地方。

0

常量池(包括字符串)是class文件中的一种数据结构(JVM之外)。当class文件被加载到JVM中时,常量池将变成运行时常量池(一般情况下),在hotspot和SE8中:

  1. 常量池中的字符串将存储在堆中,我们称之为字符串池; https://openjdk.org/jeps/122 https://wiki.openjdk.org/display/HotSpot/Caching+Java+Heap+Objects
  2. 常量池中的其他数据将存储在本地内存(元空间)中,并称其为运行时常量池(特殊情况下)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接