如果你有一个String的两个实例,并且它们是相等的,那么在Java中它们将共享相同的内存。这是如何实施的?java如何在引擎盖下实现字符串的flyweight模式?
编辑:我的应用程序使用大量的字符串对象,其中许多是相同的。使用Java String常量池的最佳方法是什么?为避免创建定制的flyweight实现?
如果你有一个String的两个实例,并且它们是相等的,那么在Java中它们将共享相同的内存。这是如何实施的?java如何在引擎盖下实现字符串的flyweight模式?
编辑:我的应用程序使用大量的字符串对象,其中许多是相同的。使用Java String常量池的最佳方法是什么?为避免创建定制的flyweight实现?
看看java.lang.String
的源代码(整个java api的源代码是JDK的一部分)。
总结:字符串包装char[]
的子序列。该支持char[]
从不修改。这是通过在String
类以外泄漏或捕获此char[]
来完成的。但是,几个Strings
可以共享相同的char[]
(请参见String.substring
的实现)。
还有一个实习机制,正如其他答案中所解释的那样。
这不是必须的。例如:
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
,给定Class
的b,
。
一个关于实习的警告词是它使用PermGen内存,这可能会导致非常讨厌的'OutOfMemoryError'。如果需要字符串池,自定义池通常是更好的选择:http://hype-free.blogspot.com/2010/03/stringintern-there-are-better-ways.html – gustafc 2010-05-26 06:37:37
从Java 7开始,实际的字符串不再在PermGen中。 Se [这个答案](http://stackoverflow.com/a/16298053/4464702)。 @gustafc – RAnders00 2016-02-11 10:39:56
字符串文字是用Java实现的,所以实际上只有一个具有多个引用的String对象(当它们相等时,情况并非总是如此)。有关更多详细信息,请参阅java.net文章All about intern()。
JLS的3.10.5 String Literals部分中还有一个很好的示例/解释,讲述了何时字符串被截取以及何时会截然不同。
如果你有一个字符串的两个实例,和他们是平等的,在Java中,他们将共享相同的存储
这其实也不是100%真实。
的为什么会是这样,什么字符串常量池的是This blog post is a decent explanation。
+1:这个答案和比尔蜥蜴的答案实际上是真正解决这个问题的。 – haylem 2012-06-18 17:18:37
要回答您编辑的问题,Sun JVM具有-XX:+StringCache
选项,在我看来,该选项可显着减少String重度应用程序的内存占用量。
否则,您可以选择实习您的字符串,但我会小心这一点。非常大且不再被引用的字符串仍然会在JVM的生命周期中使用内存。
编辑(响应评论):我首先从here发现了关于StringCache选项:
-XX:+ StringCache启用常用分配字符串缓存。
Tom Hawtin描述了某些类型的缓存以改进一些基准。当我把它放在IDEA上时,我的观察是内存占用(在完整的垃圾收集之后)没有它。这不是一个记录参数,并且确实可能只是针对某些基准进行优化。我的观察是,它有帮助,但我不会建立一个基于它的重要系统。
我试着在-XX:+ StringCache上找到更多的信息,但无济于事。我在哪里可以阅读关于这个选项的更多信息,以及它如何减少内存占用?你有更多关于这个选项对VM有用的信息吗? – Dan 2010-05-26 17:46:16
两件事情要小心:
new String("abc")
构造,只是用文字。intern()
总是返回汇集的字符串。
如果你的相同字符串来自一组固定的可能值,那么类型安全枚举就是你想要的。它不仅会减少你的字符串数量,而且还会提供更稳定的应用程序。你的整个应用程序将知道这个String有附加的语义,甚至可能有一些便利的方法。
我最喜欢的优化总是可以捍卫的代码更好,而不仅仅是更快。并且10次中有9次,用具体类型替换字符串会导致更加正确和自我记录的代码。
“String.substring”没有分配新的“char []”的事实现在不再成立。看[这个答案](http://stackoverflow.com/a/14161077/4464702)。 – RAnders00 2016-02-11 10:37:39
这是正确的。字符串不再实现flyweight模式,因为引用共享现在被认为比减少字符串的“权重”更加昂贵,部分原因是JVM已经改进以在堆栈上分配对象,如果转义分析证明对象不能超越当前堆栈帧 - 一种依赖不共享'char []'对象的优化。 – meriton 2016-02-11 22:01:43