java如何在引擎盖下实现字符串的flyweight模式?
如果你有两个String实例,并且它们是相同的,那么在Java中它们将共享相同的内存。 这是如何实现的?
编辑:我的应用程序使用大量的String对象,其中许多是相同的。 使用Java String常量池的最佳方法是什么,以避免创建自定义flyweight实现?
查看java.lang.String
的源代码(整个java api的源代码是JDK的一部分)。
总结一下:String包装了char[]
的子序列。 后备char[]
永远不会被修改。 这是通过在String
类之外泄漏或捕获此char[]
来实现的。 但是,几个Strings
可以共享相同的char[]
(请参阅String.substring
实现)。
如其他答案所述,还有实习机制。
如果你有两个String实例,并且它们是相同的,那么在Java中它们将共享相同的内存
这实际上不是100%真实。
这篇博客文章很好地解释了为什么会这样,以及String常量池是什么。
字符串文字是用Java实现的,因此实际上只有一个具有多个引用的String对象(当它们相等时,情况并非总是如此)。 有关更多详细信息,请参阅java.net文章所有关于intern()的内容。
在3.10.5 JLS的字符串文字中还有一个很好的例子/解释,它讨论了字符串何时被实现以及什么时候它们是不同的。
这不是必须的。 例:
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
现在不鼓励使用第二种forms。 有些人(包括我)认为String
甚至不应该有公共构造函数。 上述更好的版本将是:
String s1 = new String("hello").intern(); String s2 = new String("hello").intern(); System.out.println(s1 == s2); // true
显然,您不需要为常量String
执行此操作。 这是说明性的。
关于这一点的重要一点是,如果你传递一个String
或从一个函数中获取一个String
,你就不能依赖于String
是规范的 。 规范 Object
满足此等式:
a.equals(b) == b.equals(a) == (a == b)
对于给定Class
非null
实例a
, b,
。
为了回答您编辑过的问题,Sun JVM有一个-XX:+StringCache
选项,在我的观察中可以显着减少String重应用程序的内存占用。
否则,你可以选择实习你的字符串,但我会小心。 非常大且不再引用的字符串仍然会在JVM的生命周期中使用内存。
编辑(回应评论):我首先从这里发现了StringCache选项:
-XX:+ StringCache启用常用分配字符串的缓存。
Tom Hawtin描述了某种类型的缓存来改进一些基准测试。 我把它放在IDEA上时的观察结果是内存占用(在完全垃圾收集之后)没有得到它。 它不是一个记录的参数,可能确实只是针对某些基准进行优化。 我的观察是它有所帮助,但我不会建立一个基于它的重要系统。
有两点需要注意:
- 不要使用
new String("abc")
构造函数,只需使用文字"abc"
。 - 学习在String类中使用intern()方法。 特别是在将字符串连接在一起或将char数组/字节数组/等转换为字符串时。
intern()
总是返回池化的字符串。
如果您的相同字符串来自一组固定的可能值,则此处需要类型安全枚举。 它不仅会减少您的字符串数量,而且还会使应用程序更加可靠。 你的整个应用程序都知道这个String附带了语义,甚至可能是一些方便的方法。
我最喜欢的优化总是那些可以保护代码更好 ,而不仅仅是更快的优化。 10次中有9次,用具体类型替换String会导致更正确和自我记录的代码。