2010-05-26 22 views
16

如果你有一个String的两个实例,并且它们是相等的,那么在Java中它们将共享相同的内存。这是如何实施的?java如何在引擎盖下实现字符串的flyweight模式?

编辑:我的应用程序使用大量的字符串对象,其中许多是相同的。使用Java String常量池的最佳方法是什么?为避免创建定制的flyweight实现?

回答

6

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

总结:字符串包装char[]的子序列。该支持char[]从不修改。这是通过在String类以外泄漏或捕获此char[]来完成的。但是,几个Strings可以共享相同的char[](请参见String.substring的实现)。

还有一个实习机制,正如其他答案中所解释的那样。

+0

“String.substring”没有分配新的“char []”的事实现在不再成立。看[这个答案](http://stackoverflow.com/a/14161077/4464702)。 – RAnders00 2016-02-11 10:37:39

+0

这是正确的。字符串不再实现flyweight模式,因为引用共享现在被认为比减少字符串的“权重”更加昂贵,部分原因是JVM已经改进以在堆栈上分配对象,如果转义分析证明对象不能超越当前堆栈帧 - 一种依赖不共享'char []'对象的优化。 – meriton 2016-02-11 22:01:43

4

这不是必须的。例如:

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做到这一点。这是说明性的。

这个最重要的一点是,如果你通过了String或从一个函数得到一个你不能依靠String规范。一个规范Object满足这个等式:

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

用于非null实例a,给定Classb,

+2

一个关于实习的警告词是它使用PermGen内存,这可能会导致非常讨厌的'OutOfMemoryError'。如果需要字符串池,自定义池通常是更好的选择:http://hype-free.blogspot.com/2010/03/stringintern-there-are-better-ways.html – gustafc 2010-05-26 06:37:37

+0

从Java 7开始,实际的字符串不再在PermGen中。 Se [这个答案](http://stackoverflow.com/a/16298053/4464702)。 @gustafc – RAnders00 2016-02-11 10:39:56

6

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

JLS的3.10.5 String Literals部分中还有一个很好的示例/解释,讲述了何时字符串被截取以及何时会截然不同。

12

如果你有一个字符串的两个实例,和他们是平等的,在Java中,他们将共享相同的存储

这其实也不是100%真实。

的为什么会是这样,什么字符串常量池的This blog post is a decent explanation

+0

+1:这个答案和比尔蜥蜴的答案实际上是真正解决这个问题的。 – haylem 2012-06-18 17:18:37

3

要回答您编辑的问题,Sun JVM具有-XX:+StringCache选项,在我看来,该选项可显着减少String重度应用程序的内存占用量。

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

编辑(响应评论):我首先从here发现了关于StringCache选项:

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

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

+0

我试着在-XX:+ StringCache上找到更多的信息,但无济于事。我在哪里可以阅读关于这个选项的更多信息,以及它如何减少内存占用?你有更多关于这个选项对VM有用的信息吗? – Dan 2010-05-26 17:46:16

1

两件事情要小心:

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

intern()总是返回汇集的字符串。

0

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

我最喜欢的优化总是可以捍卫的代码更好,而不仅仅是更快。并且10次中有9次,用具体类型替换字符串会导致更加正确和自我记录的代码。

相关问题