21

如果您有两个 String 实例,并且它们是相等的,那么在 Java 中它们将共享相同的内存。这是如何在幕后实现的?

编辑:我的应用程序使用大量 String 对象,其中许多是相同的。使用 Java String 常量池的最佳方法是什么,以避免创建自定义享元实现?

4

7 回答 7

14

如果您有两个 String 实例,并且它们是相等的,那么在 Java 中它们将共享相同的内存

这实际上不是 100% 正确的。

这篇博文很好地解释了为什么会这样,以及字符串常量池是什么。

于 2010-05-26T03:00:48.337 回答
7

查看源代码java.lang.String(整个java api的源代码是JDK的一部分)。

总结一下: String 包装了 a 的子序列char[]。这种支持char[]永远不会被修改。这是通过char[]在课堂之外既不泄漏也不捕获它来实现的String。但是,几个Strings可以共享相同的char[](请参阅实现String.substring)。

如其他答案中所述,还有实习机制。

于 2010-05-26T20:13:45.620 回答
6

字符串字面量在 Java 中是实习的,所以实际上只有一个 String 对象具有多个引用(当它们相等时,情况并非总是如此)。有关更多详细信息,请参阅 java.net 文章All about intern()

JLS 的第3.10.5 节字符串文字中也有一个很好的示例/解释,它讨论了字符串何时被实习以及何时它们将是不同的。

于 2010-05-26T02:59:25.097 回答
5

这不一定是真的。例子:

String s1 = "hello";
String s2 = "hello";
System.out.println(s1 == s2); // true

但:

String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1 == s2); // false

现在不鼓励使用第二种形式。有些人(包括我)认为String甚至不应该有一个公共构造函数。上述更好的版本是:

String s1 = new String("hello").intern();
String s2 = new String("hello").intern();
System.out.println(s1 == s2); // true

显然,您不需要为常量执行此操作String。这是说明性的。

关于这一点的重要一点是,如果您从函数中传递了 a或从函数中获取了一个,则String您不能依赖canonical。一个规范满足这个等式:String Object

a.equals(b) == b.equals(a) == (a == b)

对于给定的非null实例。ab,Class

于 2010-05-26T02:59:15.153 回答
4

为了回答您编辑的问题,Sun JVM 有一个-XX:+StringCache选项,在我看来,它可以显着减少 String 繁重应用程序的内存占用。

否则,你可以选择实习你的字符串,但我会小心的。非常大且不再被引用的字符串仍将在 JVM 的生命周期内使用内存。

编辑(回应评论):我首先从这里发现了 StringCache 选项:

-XX:+StringCache 启用常用分配字符串的缓存。

Tom Hawtin描述了某种类型的缓存来改进某些基准。当我把它放在 IDEA 上时,我的观察是内存占用量(在完全垃圾收集之后)比没有它要低得多。它不是一个记录的参数,并且可能确实只是针对某些基准进行优化。我的观察是它有帮助,但我不会基于它构建一个重要的系统。

于 2010-05-26T03:31:17.733 回答
2

需要注意两点:

  1. 不要使用new String("abc")构造函数,只需使用文字"abc"
  2. 学习在 String 类中使用intern()方法。特别是在将字符串连接在一起或将字符数组/字节数组/等转换为字符串时。

intern()总是返回池中的字符串。

于 2010-05-26T05:04:21.480 回答
0

如果您的相同字符串来自一组固定的可能值,那么类型安全枚举就是您想要的。它不仅会减少您的字符串数量,还会使应用程序更加可靠。你的整个应用程序都会知道这个字符串附加了语义,甚至可能是一些方便的方法。

我最喜欢的优化总是可以辩护为使代码更好,而不仅仅是更快。10 次中有 9 次将 String 替换为具体类型会导致更正确和自记录的代码。

于 2010-05-27T17:29:57.493 回答