13

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

为什么我有这个疑问是,我看到实习生中没有外在状态。在 GoF 中,我读到应该在内在和外在状态之间取得适当的平衡。但在实习生中,一切都是内在的。

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

请帮我理解。

4

6 回答 6

4

不管实习如何,Java String 通过在字符串和通过类似方法调用char[]从它派生的字符串之间共享字符串来利用享元模式。substring不过,这也有不利的一面:如果你从一个巨大的字符串中提取一个小的子字符串,那么这个巨大的字符串char[]将没有资格进行垃圾收集。

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

于 2012-06-25T12:19:06.410 回答
4

是的,String.intern()实现遵循享元模式。

正如javadoc所说

返回字符串对象的规范表示。一个字符串池,最初是空的,由 String 类私下维护。

当调用 intern 方法时,如果池中已经包含一个等于该 String 对象的字符串,由 equals(Object) 方法确定,则返回池中的字符串。否则,将此 String 对象添加到池中并返回对该 String 对象的引用。

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

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

内部化的字符串驻留在“Perm Gen”空间中,并且.intern()您可以在返回的字符串对象上使用运算符==,因为.intern()对于相等的值总是返回相同的对象。

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

尝试阅读这篇文章

于 2012-06-25T12:48:31.700 回答
3

您已经正确地确定了 Interning 和 Flyweight 都基于相同的想法:缓存和共享公共状态。

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

实习是否“真的”是或不是一种蝇量级只是关于定义的争论。最重要的是理解如何将一个人视为另一个人的特殊实例,所以你很好。

于 2015-12-07T14:37:29.380 回答
0

就像其他人所说的那样, String.intern() 都是关于缓存的。它返回对池中已存储的字符串文字的引用。通过这种方式,它在某种程度上类似于享元模式,因为它使用现有对象,从而降低内存消耗并提高性能(尽管实习生在字符串池中查找也有自己的性能开销)。因此,这两个可能看起来很相似,但实际上并非如此。

于 2015-12-26T17:54:50.550 回答
0

不,共享对象以减少内存不足以称其为享元。换句话说,缓存不会自动成为享元模式。

我认为可以说享元是一种特殊的缓存形式,即部分缓存;但请注意,GoF 书在享元章节中的任何地方都没有使用“缓存”或“缓存”一词(尽管这些术语分别在前面和后面的章节中使用,门面和代理)。

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

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

    - CS

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

    ——马尔科·托波尔尼克

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

当满足以下所有条件时应用享元模式:

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

那么 4/5 的通过标准是相当不错的吧?这还不足以说明实习/缓存和轻量级是相同的吗?否:相似!= 相同。GoF 引用中对all一词的强调是他们的,而不是我的。自然而然地强烈希望用 GoF 模式名称标记尽可能多的实现,因为这样做会给这些实现带来合法性。(最令人震惊的情况是工厂模式,你可以很容易地找到可以想象的各种创建代码的标签;但我离题了。)如果模式不符合其发布的定义,它们就会重叠并失去意义,从而打败了大部分他们的目的(常用词汇)。

最后,我们来分析flyweight一章的第一句话:GoF定义为flyweight模式的Intent是什么。

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

我认为没有外在状态的对象不是细粒度的,而是相反的;所以这里有一个建议的缓存意图:使用缓存来有效地支持大量粗粒度对象。

显然,String interning/caching 和 Flyweight Pattern 有相似之处;但它们不一样。

于 2018-10-19T22:19:45.373 回答
-1

Flyweight 是关于共享对象不可变的内部结构。实习只是缓存整个对象。

于 2012-11-19T23:47:16.417 回答