Java: String intern() 和 StringPool 如何工作?

3
根据 String.intern() 的 Javadoc:
当调用intern方法时,如果池中已经包含与该String对象相等的字符串(由equals(Object)方法确定),则返回来自池中的字符串。否则,将该String对象添加到池中并返回对该String对象的引用。
我有几个问题:
1. 当创建一个新的String对象(不使用字符串字面值,而是使用new()运算符)时,如下所示: ``` String str = new String("Test"); ```
问题:我知道在堆中会创建一个新对象。但是,在对象创建过程中是否也会将字符串 Test 放入字符串池中?如果是,为什么不直接返回字符串池中的引用。如果不是,为什么不直接将字符串放入池中,现在StringPool已经从PermGen中移出,并且处于常规堆空间中(即除了堆空间限制外没有其他空间限制)。有一些帖子声称在创建对象时立即将字符串插入池中,而也有一些帖子与此相矛盾。
  1. 当我们在 String 对象上调用 String.intern()(因为字面量已经被 interned),对象分配的空间会发生什么?它会在同一时刻被回收,还是等待下一个 GC 循环?

  2. 在 SO 上 另一个问题 的被接受的答案指出,当你需要速度时应该使用 String intern,因为你可以通过引用比较字符串(== 比 equals 更快)。

问题:我知道使用 String.intern() 时,它返回对已存在于 StringPool 中的字符串的引用。但这需要在 StringPool 上进行全扫描查找,这本身可能是昂贵的操作。那么,在字符串比较期间实现的这种速度是否合理?如果是,为什么?

我已经查看了以下来源:


3
为什么你会认为字符串查找操作很耗费资源呢?我想它应该是一个O(1)的操作,因为在其背后肯定有一个类似于哈希表的数据结构。 - Kayaman
1
你正在将"Test"传递给构造函数,并且引用字符串字面量"Test"会将其放入字符串池中。然后,您调用new,这明确要求Java制作副本并返回该副本,而不是字符串池中的引用。 - Louis Wasserman
@LouisWasserman 好的,那肯定有道理。但我现在更好奇的是为什么要返回一个新对象。这有什么用处呢?为什么不返回指向池中同一对象的引用,这样可以加快比较速度,并且它们都是不可变的,所以占用相同的堆空间?(除非在某个地方需要具有相同值但不同引用的对象,但不确定在哪里可能需要) - Aaditya Gavandalkar
这并不实用。这就是为什么几乎没有真正的代码使用那个String构造函数。 - Louis Wasserman
@LouisWasserman 感谢您的解释 :) - Aaditya Gavandalkar
显示剩余4条评论
1个回答

0
  1. 所有字符串字面量都在编译时进行了内部化。使用一个字符串字面量作为单参数构造函数的参数有点滥用该构造函数,因此你可能会得到两个字符串(但也许有特殊的编译器情况,我不能确定)。截至Java 8,该构造函数的实现(对于OpenJDK)如下:
public String(String original) {
    this.value = original.value;
    this.hash = original.hash;
}

因此,在这一方面没有特别的处理。如果你知道字面意思,不要使用这个构造函数。

  1. 我认为字符串没有任何特殊的垃圾回收语义。只要它变得不可达并被 GC 判定为值得回收的对象,它就会像任何其他对象一样被回收。

  2. 永远不要使用“==”来比较字符串,“String”默认的“equals”方法的第一步就是这样做。如果这是您主要的情况(您知道大多数时间都在使用已经 interned 的字符串),那么您只需要支付一个微小的方法调用开销,而通过执行这样的操作添加未来错误的潜在风险则太大了。


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