Java字符串字面量池和字符串对象

11

我知道JVM维护字符串字面量池来提高性能并维护JVM内存,并学习了字符串字面量的维护方式。但是我想澄清与字符串池和在堆上创建的字符串对象相关的一些内容。

如果我的解释有误,请指出。

String s = "abc";
如果上述代码行被执行,那么如果字符串字面量在池中不存在,则将其添加到字符串池中。此外,在堆上创建了一个字符串对象,并且引用 s 将指向池中的字面量。
问题:
  1. 每次执行此代码时,它是否都会在堆上创建字符串对象?
  2. 字符串字面量池只维护字符串字面量还是也维护字符串对象?
  3. JVM 何时决定将字符串字面量添加到字符串池中?它是在编译时还是运行时决定的?
我不确定如果引用指向池中的字符串字面量时字符串对象会在哪里创建。
谢谢。
4个回答

5

没有“字面常量池”。字符串池只是普通的堆对象。他们可能会出现在PermGen,但即使如此,它们也可能被垃圾回收。

类文件有一个常量池,其中包含类中使用的字符串字面量。当加载类时,从该数据创建字符串对象,这与String#intern所做的非常相似。

这段代码每次执行时都会在堆上创建字符串对象吗?

不是的。将重用一个字符串对象。它已经在加载类时被创建了。

字符串字面量池仅维护字符串字面量还是也维护字符串对象?

您也可以将字符串加入池中。我认为它们会被视为差不多相同。

JVM 何时决定需要将字符串字面量添加到字符串池中?编译时还是运行时?

字面值始终“汇集”在一起。其他字符串需要调用“intern”。因此,在某种程度上,决策是在编译时做出的。


如果 interned 字符串留在 PernGen 中,那么使用 "new" 关键字创建的字符串对象呢?它是在年轻代中创建的吗? - user826323
1
所有新对象都是在年轻代中创建的。如果它们存活时间足够长,它们将迁移到其他地方。 - Thilo
当类被加载时,从该数据创建String对象,这与String#intern所做的非常相似。但是,字符串字面量 - 或更一般地说,作为常量表达式值(§15.28)的字符串 - 被“内部化”以共享唯一实例,使用方法String.intern。(规范) - T.J. Crowder

4
引用String.intern()文档(强调是我的
所有字面字符串和字符串常量表达式都会被池化。字符串字面量在Java语言规范的§3.10.5中定义。
类String私有地维护着一个最初为空的字符串池。
当调用intern方法时,如果池中已经包含一个与该字符串对象相等的字符串(由equals(Object)方法确定),则返回池中的字符串。否则,将该字符串对象添加到池中,并返回对该字符串对象的引用。
因此,对于每个唯一的池化字符串,只创建一个对象。所有引用共享这个不可变对象。
没有“字面对象”。字面字符串表达式在转换时,存储为常规的String对象。此外,池包含所有池化的字符串对象。包括隐式的(通过使用字符串字面量表达式)和显式的(通过在String对象上调用.intern())。
我不确定JVM何时决定将字符串字面量添加到字符串池中。

JVM并不决定,编译器在编译时决定。 - user207421

1

我认为你忽略了一些基本的东西:字符串池包含字符串对象。字面量不是某种特殊的对象;在运行时,它们只是另一个字符串对象。

此外,您可以使用String.intern()来内部化任何想要的字符串;它不必来自字面量。

所以关于你的问题:

  1. 不,当类被加载时只会分配一个字符串对象。
  2. 它不维护任何字面量,而是维护了被内部化的字符串对象。通常,这些来自字面量,但实际上它可以是任何编译时常量表达式(String constant = "abc" + "def"将在运行时产生一个字符串对象"abcdef")。
  3. 它们被编译到类文件中。因此,它们在编译时被决定,但显然对象本身是在运行时创建的。

1
“字面量并不是某种特殊的对象;在运行时,它们只是另一个字符串对象。” 字面量根本不是对象;它们只是一种在源代码中编写对象的方式(在这种情况下)。字面量的概念是源代码的概念。字面量的结果是一个 String 对象。(您稍后很好地做出了这个区分。) - T.J. Crowder

1

这段代码每次执行时都会在堆上创建字符串对象吗?

不是的。它只会在文字池中创建一次,然后反复引用。

字符串文字池只维护字符串文字还是也维护字符串对象?

所有都是对象,但是通过赋值创建的对象被放入池中,而通过new操作符创建的对象被放在堆上。

JVM 何时决定将字符串文字添加到字符串池中?它是在编译时还是运行时决定的?

无论何时 JVM 遇到像

String str="Hello"; (string literal) or
String str="Hel" + "lo"; (string constant expression).

如果结果字符串(在这种情况下为str)不是池中的字符串,则在所有这些情况下,它会在池中添加新字符串。当然,这发生在运行时。

查看this链接。


不是的。当JVM执行代码时,像您回答中那样看到字符串字面量时,这些字面量已经被编译器和类加载器放置在内部。常量表达式在编译时被评估。 - user207421
请提供任何支持链接/文章/示例以证明您的观点。 - Santosh

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