问题 Java String文字池和字符串对象


我知道JVM维护一个字符串文字池来提高性能并维护JVM内存,并了解到字符串文字是在字符串池中维护的。但我想澄清与堆上创建的字符串池和字符串对象相关的内容。

如果我的解释错误,请纠正我。

String s = "abc";

如果执行上面的行,如果池中不存在“abc”字符串文字,则将其添加到字符串池中。并在堆上创建字符串对象和引用 s 将指向池中的文字。

问题:

  1. 这段代码每次执行时都会在堆上创建字符串对象吗?
  2. 字符串文字池是仅维护字符串文字还是维护字符串对象?
  3. JVM何时决定是否需要将字符串文字添加到字符串池中?它是在编译时还是运行时决定的?

如果它指向池中的字符串文字,我不确定创建字符串对象的确切位置。

谢谢。


2909
2017-11-08 04:44


起源



答案:


没有“文字池”。 Interned Strings只是普通的堆对象。它们最终可能会进入PermGen,但即便如此,它们最终也可能被垃圾收集。

类文件有一个常量池,它包含类中使用的字符串文字。加载类时,会根据该数据创建String对象,这可能与String #intern的内容非常相似。

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

不会。将有一个正在重用的String对象。它已在加载类时创建。

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

你也可以实习字符串。我认为他们的待遇大致相同。

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

文字总是“汇集”。其他字符串需要在其上调用“实习生”。所以在某种程度上,决定是在编译时做出的。


5
2017-11-08 04:56



如果实习字符串保留在PernGen中,那么使用“new”关键字创建的字符串对象怎么样?它是在年轻一代创造的吗? - user826323
所有新对象都是在年轻一代中创建的。如果他们活得足够长,他们就会迁移到其他地方。 - Thilo
“当加载类时,String对象是从该数据创建的,这可能与String #intern的做法非常相似。” 不仅仅是类似的: “...字符串文字 - 或者更一般地说,作为常量表达式的值的字符串(第15.28节) - 被”实习“以便使用该方法共享唯一实例 String.intern“。  (SPEC) - T.J. Crowder


答案:


没有“文字池”。 Interned Strings只是普通的堆对象。它们最终可能会进入PermGen,但即便如此,它们最终也可能被垃圾收集。

类文件有一个常量池,它包含类中使用的字符串文字。加载类时,会根据该数据创建String对象,这可能与String #intern的内容非常相似。

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

不会。将有一个正在重用的String对象。它已在加载类时创建。

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

你也可以实习字符串。我认为他们的待遇大致相同。

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

文字总是“汇集”。其他字符串需要在其上调用“实习生”。所以在某种程度上,决定是在编译时做出的。


5
2017-11-08 04:56



如果实习字符串保留在PernGen中,那么使用“new”关键字创建的字符串对象怎么样?它是在年轻一代创造的吗? - user826323
所有新对象都是在年轻一代中创建的。如果他们活得足够长,他们就会迁移到其他地方。 - Thilo
“当加载类时,String对象是从该数据创建的,这可能与String #intern的做法非常相似。” 不仅仅是类似的: “...字符串文字 - 或者更一般地说,作为常量表达式的值的字符串(第15.28节) - 被”实习“以便使用该方法共享唯一实例 String.intern“。  (SPEC) - T.J. Crowder


引用文档 中的String.intern()强调我的

所有文字字符串和字符串值常量表达式都是   实习。字符串文字定义在 §3.10.5 Java语言   规范


最初为空的字符串池由私有维护   class String。

调用实习方法时,如果池已包含   字符串等于此字符串对象,由equals(Object)确定   方法,然后返回池中的字符串。否则,这个   String对象被添加到池中以及对此String的引用   返回对象。


从而,

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

仅为每个唯一的实习字符串创建一个对象。所有引用都共享此不可变对象。

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

没有'文字对象'。转换后的文字字符串表达式存储为常规 String objects.Also,池包含所有interned字符串对象。隐式(通过使用字符串文字表达式)和显式(通过调用 .intern()在...上 String 目的)。

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

我不确定。


4
2017-11-08 05:08



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


我认为你缺少一些基本的东西:实习字符串池 只要 包含String对象。文字不是某种特殊的对象;在运行时,它们只是另一个String对象。

另外,您可以实习您想要使用的任何字符串 String.intern();它不一定来自文字。

关于你的问题:

  1. 不,在加载类时会分配一个String对象。
  2. 它没有维持 任何 文字,而是实习的字符串对象。通常,那些来自文字,但实际上它可以是任何编译时常量表达式(String constant = "abc" + "def" 会在运行时导致一个String对象“abcdef”。
  3. 它们被编译到类文件中。所以它们是在编译时决定的,但显然对象本身是在运行时创建的。

1
2017-11-08 04:53



“文字不是某种特殊对象;在运行时它们只是另一个String对象。”  字面 根本不是对象;它们是在源代码中编写对象(在本例中)的一种方式。文字的概念是源代码概念。该 结果 文字是一个 String 目的。 (你稍后会很好地区分它。) - T.J. Crowder


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

不。一旦在文字池中创建。同样一次又一次地提到。

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

所有都只是对象,但通过赋值创建的对象放在池中,通过它创建 new 运算符放在堆上。

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

每当JVM遇到类似的表达式时

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

和结果串(str 在这种情况下)不是池,然后在所有这些情况下,它在池中添加新字符串。这个过程在运行时发生。

查看 这个 链接。


1
2017-11-08 04:58



没有。当JVM在执行代码时看到字符串文字时,例如在你的答案中,文字已被实习,由编译器和类加载器放置在那里。在编译时计算常量表达式。 - user207421
请提供任何证明链接/文章/示例以证明您的观点。 - Santosh
该 Java语言规范#3.10.5。但它是 您 谁应该支持 你的 从权威来源引用引文。 - user207421