【问题标题】:Compiler behavior with String literals to Create String Constant Pool使用字符串文字创建字符串常量池的编译器行为
【发布时间】:2018-06-21 04:25:40
【问题描述】:

阅读这些讨论后 - question 1question 2article

我对Java字符串常量池的理解如下(如有错误请指正):

编译源代码时,编译器会在我们的程序中查找所有字符串字面量(放在双引号中的字面量),并在堆区域中创建不同(无重复)的对象,并将它们的引用保存在一个名为String Constant Pool方法区域内的一个区域)。任何其他字符串对象都是在运行时创建的。

假设我们的代码有以下语句:

String a = "abc";                  //Line 1
String b = "xyz";                  //Line 2
String c = "abc";                  //Line 3
String d = new String("abc"):      //Line 4

上述代码编译完成后,

第 1 行:在堆中创建了一个字符串对象“abc”,该对象由变量 a 和字符串常量池引用。

第 2 行: 编译器在字符串常量池中搜索对对象“xyz”的任何现有引用。但是没有找到。因此,它创建对象“xyz”并将其引用放入字符串常量池中。

第 3 行: 这一次编译器在字符串常量池中找到对象,并且不会在池或堆中添加任何额外的条目。变量 c 只是引用现有的对象,它也被 a 引用。

第 4 行: 第 4 行中的文字出现在字符串常量池中。因此,池中不再有条目。然而,在运行时会为“abc”创建另一个 String 对象,并将其引用存储在变量 d 中。

现在我有以下问题/疑问:

  1. 上面描述的究竟是什么情况?
  2. 编译器如何创建对象?据我所知,对象 在运行时创建,堆是运行时内存区域。又怎样 以及在哪里创建 String 对象 编译!
  3. 源代码可以在一台机器上编译并在不同的机器上运行 机器。或者,即使在同一台机器上,它们也可以编译和运行 不同的时间。然后这些对象(在编译时创建)是如何 康复了吗?
  4. 当我们实习字符串时会发生什么。

【问题讨论】:

标签: java string memory-management compilation heap-memory


【解决方案1】:
  1. 上面描述的究竟是什么情况?

是的,但是从概念上讲,常量池和字符串池是不同的东西。

constant pool.class 文件的一部分,该文件包含此类中使用的所有常量。

string pool 是一个运行时概念 - 内部字符串和字符串文字 are stored here

这是常量池上的JVM specification。它是.class 格式部分的一部分。

  1. 编译器如何创建对象?据我所知,对象是在运行时创建的,而堆是运行时内存区域。那么,String 对象在编译时是如何以及在哪里创建的!

确切地发生这种情况的方式/时间,我相信,是 JVM 实现特定的细节(如果我错了,请纠正我),但基本解释是,每当 JVM 决定加载一个类时,在 constant pool 中找到的任何字符串都会自动放入 runtime string pool,并且任何重复项都会引用同一个实例。

在其中一个链接答案的 cmets 中,Paŭlo Ebermann says

当类在 VM 中加载时,字符串常量将被复制到堆中,复制到 VM 范围的字符串池中

看来这至少是 Sun 的 VM 实现字符串池的方式。

在 JDK 7/HotSpot 之前,实习字符串存储在 permanent generation space 中 - 现在它们存储在主堆中。

  1. 源代码可以在一台机器上编译并在另一台机器上运行。或者,即使在同一台机器上,它们也可以在不同的时间编译和运行。那么这些对象(在编译时创建的)是如何恢复的呢?

常量存储在编译文件中。因此,只要 JVM 决定加载此类,它们就可以检索。

  1. 当我们实习字符串时会发生什么。

这是answered here

对一系列字符串执行String.intern() 将确保所有具有相同内容的字符串共享相同的内存

【讨论】:

  • 如果我理解正确的话,字符串文字存储在.class 文件中,类似于任何其他数据。当 JVM 加载该类时,它将这些字符串数据存储在字符串池中。在加载下一个类时,它只会将不存在的字符串放入池中。但是,stackoverflow.com/a/1881936/3019006 这个答案指出“Java 编译器足够聪明,可以制作..”这是什么意思?如果由 JVM 完成,编译器将如何发挥作用?
  • @Deb 我认为这是一个错误的措辞——编译器所做的只是将字符串放入常量池中,并使用这个常量而不是调用new String(...)。 JVM 负责将内部字符串放入堆中。
  • 如果是这样的话。然后假设我有A.java包含一个字符串“abc”和B.java也包含“abc”。那么,“abc”将存储在A.classB.class,或者两者都有?如果,它将字符串存储在两个类文件的常量池。那么,不创建冗余字符串的目的是如何实现的呢?
  • @Deb "abc" in binary 将存储在两个 .class 文件中,但是一旦加载了两个类,(运行时)字符串池将只包含一个 " abc”字符串。对“abc”的两个引用都将引用这个字符串。
  • @Deb:如果一个类包含多次出现的相同字符串常量,例如"abc",它们通常被编译为类常量池的单个条目。规范并不要求它,但所有常用的编译器都会这样做,因为它缩短了类文件并减少了运行时的工作。
猜你喜欢
  • 2014-09-14
  • 1970-01-01
  • 2012-12-18
  • 1970-01-01
  • 2014-06-08
  • 1970-01-01
  • 1970-01-01
  • 2023-03-22
  • 2020-01-09
相关资源
最近更新 更多