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

java如何为引擎盖下的字符串实现flyweight模式?,java,design-patterns,flyweight-pattern,Java,Design Patterns,Flyweight Pattern,如果一个字符串有两个实例,并且它们相等,那么在Java中它们将共享相同的内存。这是如何在引擎盖下实现的 编辑:我的应用程序使用大量的字符串对象,其中许多是相同的。使用Java字符串常量池以避免创建自定义flyweight实现的最佳方法是什么?这是不必要的。例如: String s1 = "hello"; String s2 = "hello"; System.out.println(s1 == s2); // true 但是: 现在不鼓励第二种形式。有些人(包括我)认为String甚至不应该有

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


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

这是不必要的。例如:

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

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

String s1 = new String("hello").intern();
String s2 = new String("hello").intern();
System.out.println(s1 == s2); // true
显然,对于常量
字符串
,不需要这样做。这是说明性的

重要的一点是,如果传递了
字符串
或从函数中获取一个字符串,则不能依赖
字符串
的规范性。规范的
对象
满足此等式:

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

对于非
null
实例
a
b,
给定
类的
,字符串文本在Java中被插入,因此实际上只有一个字符串对象具有多个引用(当它们相等时,情况并非总是如此)。有关更多详细信息,请参阅java.net文章

在JLS的一节中还有一个很好的示例/解释,它讨论了字符串何时被插入以及何时被区分

如果一个字符串有两个实例,并且它们相等,那么在Java中它们将共享相同的内存

这实际上不是100%正确的


为什么会这样,以及字符串常量池是什么。

为了回答您编辑的问题,Sun JVM有一个
-XX:+StringCache
选项,据我观察,它可以显著减少字符串密集型应用程序的内存占用

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

编辑(回应评论):我首先从以下网站了解到StringCache选项:

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


描述一些类型的缓存以改进某些基准测试。当我把它放在IDEA上时,我观察到内存占用(在一次完整的垃圾收集之后)比没有内存占用要小得多。它不是一个文档化的参数,实际上可能只是一些基准的优化。我的观察结果是,它有帮助,但我不会在此基础上构建一个重要的系统。

有两件事需要注意:

  • 不要使用
    新字符串(“abc”)
    构造函数,只需使用文本
    “abc”
  • 学习在字符串类中使用方法。尤其是将字符串连接在一起或将字符数组/字节数组/etc转换为字符串时

  • intern()
    始终返回合并的字符串。

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

    总结:字符串包装了
    char[]
    的子序列。这个backing
    char[]
    永远不会被修改。这是通过在
    字符串
    类之外既不泄漏也不捕获此
    char[]
    来实现的。但是,多个
    字符串
    可以共享相同的
    char[]
    (请参见
    String.substring的实现)


    正如其他答案中所解释的,还有一种插入机制。

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


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

    关于实习的一个警告是,它使用PermGen内存,这可能会导致非常严重的
    OutOfMemoryError
    。如果字符串池是必要的,那么定制池通常是更好的选择:从Java7开始,插入的字符串不再在PermGen中。嗯@gustafcI尝试在-XX:+StringCache上查找更多信息,但没有结果。我在哪里可以了解更多关于此选项的信息,以及它如何减少内存占用?你有关于这个选项对VM做了什么的更多信息吗?+1:这个答案和蜥蜴比尔的答案实际上是解决这个问题的答案。
    String.substring
    没有分配新的
    char[]
    这一事实现在不再成立。看,这是正确的。String不再实现flyweight模式,因为引用共享现在被认为比减少字符串的“重量”更昂贵,部分原因是JVM得到了改进,如果转义分析证明对象不能超过当前堆栈帧,就可以在堆栈上分配对象—这种优化依赖于不共享
    char[]
    对象。
    a.equals(b) == b.equals(a) == (a == b)