Java的字符串实习生是轻量级吗?

时间:2012-06-25 12:12:38

标签: java design-patterns flyweight-pattern

Java的String内存池的实现是否遵循flyweight模式?

为什么我有这个疑问,我看到实习生没有涉及外在国家。在GoF我读到内在和外在状态之间应该有一个正确的平衡。但在实习生中,一切都是固有的。

或者我们应该说没有关于属性的严格规则,只是共享对象以减少内存就足以称之为flyweight。

请帮助我理解。

6 个答案:

答案 0 :(得分:3)

无论是否实习,Java String都会通过char[]和类似的方法调用在字符串和从字符串派生的字符串之间共享substring来使用flyweight模式。但是,这有另一个方面:如果你接受一个巨大字符串的一个小子串,那么巨大的char[]就没有资格进行垃圾收集。

注意:从OpenJDK版本1.7.0_06开始,上述内容已经过时:代码已更改,因此实例之间不再共享char[]substring()创建一个新数组。

答案 1 :(得分:3)

String.intern()实现遵循flyweight模式。

正如javadoc所说

  

返回字符串对象的规范表示。一池水   最初为空的字符串由String类私有维护。

     

当调用实习方法时,如果池已包含   字符串等于此字符串对象由equals(Object)确定   方法,然后返回池中的字符串。否则,这个   String对象被添加到池中以及对此String的引用   返回对象。

     

对于任何两个字符串s和t,s.intern()== t.intern()   当且仅当s.equals(t)为真时才为真。

     

所有文字字符串和字符串值常量表达式都是   实习。字符串文字在Java语言的§3.10.5中定义   说明书

内化字符串驻留在“Perm Gen”空间和.intern()返回的字符串对象上,您可以使用运算符==,因为.intern()始终返回相同值的相同对象。

然后记住.intern()方法不会产生泄漏,因为今天的JVM可以对池进行垃圾处理。

尝试阅读此article

答案 2 :(得分:3)

您已正确识别Interning和Flyweight都基于相同的想法:缓存和共享共同状态。

使用Flyweight,在没有外部状态存储的极端情况下,只有指向内部状态的指针仍然存在。那么外在状态甚至不需要成为一个对象,指针本身也可以是外在状态。当Flyweight成为实习生时。

是否实习"真的"是或不是一种Flyweight只是对定义的争论。最重要的是理解如何将一个人视为另一个人的特殊实例,这样你就会很好。

答案 3 :(得分:0)

就像其他人所说,String.intern()就是缓存。它返回对池中已存储的字符串文字的引用。通过这种方式,它与flyweight模式类似,因为它使用现有对象,从而降低了内存消耗并提高了性能(尽管实习生在字符串池中也有自己的查找性能开销)。因此,这两者似乎相似,但实际上并非如此。

答案 4 :(得分:0)

否,共享对象以减少内存不足以称其为“轻量级”。换句话说,缓存不会自动成为“举重”模式。

我认为公平地说,flyweight是一种特殊的缓存形式,即部分缓存;但请注意,GoF手册在flyweight章节的任何地方都没有使用“ cache”或“ caching”一词(尽管上一章和后续章节分别使用了术语“ facade”和“ proxy”)。

此线程中的一些评论值得重复,因为它们简洁地回答了整个问题。

  •   

    如果您的对象没有外部上下文,那么您只是在缓存。 Flyweight模式甚至有用的全部原因   定义是人们经常忘记他们至少可以缓存一部分   与上下文无关的对象并共享它。

         

    -CS

  •   

    Flyweight是关于共享对象内部的。实习只是缓存整个对象。

         

    -马尔科·托波尔尼克

但是让我们将String实习与GoF定义的标准进行比较(第197页)。

  

当以下所有 all 为真时,应用Flyweight模式:

     
      
  • 应用程序使用大量对象。
  •   
  • 由于对象数量巨大,因此存储成本很高。
  •   
  • 大多数对象状态可以是外部的。
  •   
  • 一旦移除外部状态,许多对象组可能会被相对较少的共享对象所代替。
  •   
  • 该应用程序不依赖于对象标识。由于可以共享轻量级对象,因此对于概念上不同的对象,身份测试将返回true。
  •   
  1. 很显然,许多应用程序都使用大量的String,因此此条件可以通过。
  2. 至少与原始类型相比,存储字符串是昂贵的,因此让我们通过此条件。
  3. 在这里我们被绊倒了:字符串状态的 none 都是外在的。此条件失败。
  4. 如果我们很慷慨,而忽略了关于外部状态的部分,那么我们也可以通过此标准,因为字符串确实易于重用。
  5. 曾经使用==比较Java中的字符串的任何人都知道不依赖于对象身份,因此该标准可以通过。

4/5合格标准相当不错吧?难道不是说interning / caching和flyweight都一样吗?否:相似!=相同。 GoF引用中对单词 all 的强调是他们的,而不是我的。自然,人们强烈希望使用GoF模式名称标记尽可能多的实现,因为这样做会使这些实现具有合法性。 (最糟糕的情况是工厂模式,您可以轻松地找到各种可以想象的创建代码标签;但是我离题了。)如果这些模式不符合其已发布的定义,它们就会重叠并失去意义,从而破坏了大部分他们的目的(常用词汇)。

最后,让我们分析一下“举重”一章的第一句话:GoF定义为“举重”模式的 Intent

  

使用共享有效地支持大量细粒度对象。

我认为没有外部状态的对象不是细粒度的,而是相反的。因此建议使用以下 Intent 进行缓存:使用缓存可以有效地支持大量的粗粒度对象。

很明显,String插入/缓存和Flyweight模式之间有相似之处;但是它们不一样。

答案 5 :(得分:-1)

Flyweight是关于共享对象immutables内部。实习只是缓存整个对象。