Java 带有字符串构造函数和内部函数的字符串池

Java String Pool with String constructor and the intern function

我最近了解了 Java 字符串池,但有几件事我不太明白。

使用赋值运算符时,如果字符串池中不存在新字符串,则会在其中创建一个新字符串。

String a = "foo"; // Creates a new string in the String Pool
String b = "foo"; // Refers to the already existing string in the String Pool

使用 String 构造函数时,我了解到无论 String Pool 的状态如何,都会在 String Pool 之外的堆中创建一个新字符串。

String c = new String("foo"); // Creates a new string in the heap

我在某处读到,即使在使用构造函数时,也会使用字符串池 。它会将字符串插入到字符串池到堆中。

String d = new String("bar"); // Creates a new string in the String Pool and in the heap

我没有找到关于此的任何进一步信息,但我想知道这是不是真的。

如果确实如此,那么 - 为什么?为什么 java 创建这个重复的字符串?这对我来说似乎完全多余,因为 java 中的字符串是不可变的。

我想知道的另一件事是字符串 class 的 .intern() 函数是如何工作的:它只是 return 指向字符串池中字符串的指针吗?

最后,在以下代码中:

String s = new String("Hello");
s = s.intern();

垃圾收集器是否会从堆中删除字符串池之外的字符串?

Q: I read somewhere that even when using the constructor, the String Pool is being used. It will insert the string into the String Pool and into the heap. [] I didn't find any further information about this, but I would like to know if that's true.

这不是真的。使用 new 创建的字符串不会放入字符串池中......除非明确调用 intern()

Q: Why does java create this duplicate string?

因为JLS规定new生成一个新对象。如果没有(IMO),它将是 counter-intuitive。

使用 new String(String) 几乎总是一个坏主意这一事实并不是让 new 在这种情况下表现不同的好理由。真正的答案是程序员应该学会不写那个……除非在极少数情况下有必要这样做。


Q: Another thing that I would like to know is how the intern() function of the String class works: Does it just return a pointer to the string in the String Pool?

intern 方法总是 return 指向字符串池中字符串的指针。该字符串可能是也可能不是您调用的字符串 intern() 或。

字符串池的实现方式多种多样。

  • 在最初的方案中,驻留字符串被保存在一个称为 PermGen 堆的特殊堆中。在该方案中,如果您正在实习的字符串不在池中,那么将在 PermGen space 中分配一个新字符串,并且 intern 方法将 return 那。

  • 在当前方案中,驻留字符串保存在普通堆中,字符串池只是一个(私有)数据结构。当被驻留的字符串不在池中时,它只是链接到数据结构中。不需要分配新的字符串。


Q: Will the garbage collector delete the string that is outside the String Pool from the heap?

规则对于 所有 Java 对象都是相同的,无论它们是如何创建的,也不管它们在哪里(其中“space”或 JVM 中的“堆”)它们驻留。

If an object is not reachable from the running application, then it is eligible for deletion by the garbage collector.

这并不意味着无法访问的对象 在 GC 的任何特定 运行 中被垃圾收集。 (或者确实曾经……在某些情况下。)

上述规则同样适用于对应字符串文字的String对象。如果文字有可能永远无法再次使用,那么它可能被垃圾收集。

这通常不会发生。 JVM 在与定义它的 class 关联的私有数据结构中保留对每个字符串文字对象的隐藏引用。由于 classes normally 在 JVM 的生命周期内存在,因此它们的字符串文字对象仍然可以访问。 (这是有道理的......因为应用程序可能需要使用它们。)

但是,如果使用动态创建的 classloader 加载 class,并且该 classloader 变得不可访问,那么它的所有 classes 也将无法访问.因此,字符串文字对象实际上有可能变得无法访问。如果是,它可能 被垃圾收集。

你写了

String c = new String("foo"); // Creates a new string in the heap

I read somewhere that even when using the constructor, the String Pool is being used. It will insert the string into the String Pool and into the heap.

说的有点对,但是你必须正确阅读代码。您的代码包含两个 String 个实例。首先,您有字符串文字 "foo",它的计算结果为 String 实例,该实例将被插入到池中。然后,您将使用 new String(…) 调用 String(String) constructor 显式创建一个新的 String 实例。由于显式创建的对象不能与其创建之前存在的对象具有相同的身份,因此必须存在两个 String 个实例。

Why does java create this duplicate string? It seems completely redundant to me since the strings in java are immutable.

它确实如此,因为你是这么告诉它的。从理论上讲,这种结构可以得到优化,跳过你无论如何都看不到的中间步骤。但是程序行为的第一个假设应该是它完全按照您编写的方式执行。

你可能会问为什么有一个构造函数允许这种毫无意义的操作。事实上,这已经被问过, 解决了这个问题。简而言之,这主要是历史设计错误,但由于其他技术原因,该构造函数已在实践中使用;有些不再适用。不过,它不能在不破坏兼容性的情况下删除。

String s = new String("Hello");
s = s.intern();

Will the garbage collector delete the string that is outside the String Pool from the heap?

由于 intern() 调用将评估为 "Hello" 创建的实例并且不同于通过 new String(…) 创建的实例,后者在第二次分配给 s。当然,这并没有说垃圾收集器 是否会 回收字符串的内存只是允许这样做。但请记住,大部分堆占用将是保存字符数据的数组,这些数据将在两个字符串实例之间共享(除非您使用非常过时的 JVM)。只要两个字符串中的任何一个在使用中,这个数组就仍然在使用中。最近的 JVM 甚至具有 功能,这可能会导致 JVM 中具有相同内容的其他字符串使用此数组(以允许收集他们以前使用的数组)。所以数组的生命周期是完全不可预测的。