java如何在引擎盖下实现字符串的flyweight模式?

时间:2010-05-26 02:51:13

标签: java design-patterns flyweight-pattern

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

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

7 个答案:

答案 0 :(得分:12)

  

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

这实际上并非100%正确。

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

答案 1 :(得分:6)

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

在JLS的3.10.5 String Literals部分中还有一个很好的例子/解释,它讨论了字符串何时被实现以及什么时候它们是不同的。

答案 2 :(得分:6)

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

总结:String包含char[]的子序列。永远不会修改后备char[]。这是通过在char[]类之外泄漏或捕获此String来实现的。但是,有几个Strings可以共享相同的char[](请参阅String.substring的实施)。

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

答案 3 :(得分: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 规范 canonical Object满足这种平等:

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

表示给定null的非a个实例b,Class

答案 4 :(得分:3)

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

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

编辑(回应评论):我首先发现了here中的StringCache选项:

  

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

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

答案 5 :(得分:1)

要注意两件事:

  1. 请勿使用new String("abc")构造函数,只需使用文字"abc"
  2. 学习在String类中使用intern()方法。特别是在将字符串连接在一起或将char数组/字节数组/ etc转换为字符串时。
  3. intern()始终返回汇总的字符串。

答案 6 :(得分:0)

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

我最喜欢的优化总是那些可以保护代码更好的优化,而不仅仅是更快。 10次​​中有9次,用一个具体类型替换一个String会导致更正确和自我记录的代码。