【问题标题】:Java 8 String deduplication vs. String.intern()Java 8 字符串重复数据删除与 String.intern()
【发布时间】:2015-12-27 13:59:05
【问题描述】:

我正在阅读 Java 8 更新 20 中用于字符串重复数据删除 (more info) 的功能,但我不确定这是否基本上会使 String.intern() 过时。

我知道这个 JVM 功能需要 G1 垃圾收集器,这可能不是许多人的选择,但假设使用 G1GC,JVM 自动重复数据删除有什么区别/优点/缺点与手动必须intern 你的字符串(一个明显的优点是不必通过调用intern() 来污染你的代码)?

考虑到 Oracle 可能将 G1GC 设为 java 9 中的默认 GC,这尤其有趣

【问题讨论】:

  • Suggested video -- 但无论如何,结论总是一样的:你。应该。不是。关心。
  • 对不起,应该不在乎什么?关于使用哪个(意味着它们是等价的)或关于新功能(意味着它没那么有用)??
  • 含义:随便使用String类就行了。
  • 好问题。将这些功能添加到 JVM 中暗示了开发人员专注于编码而不是内存管理。您不应该使用 String.intern()System.gc() - 让 VM 完成它的工作。
  • @DavidConrad 在完整的引用中,Knuth 实际上量化了他的建议:“我们应该忘记小的效率,比如说大约 97% 的时间。过早的优化是万恶之源”。所以 Knuth 确实关心低级性能(非常关心 - 请参阅编程艺术中的详细信息)和 3% 的代码(整个应用程序)将从一些关注和调整中受益。但是,是的,要点:一周中的任何一天都优先考虑自己的时间而不是 CPU 时间。 :-)

标签: java string jvm-hotspot deduplication


【解决方案1】:

使用此功能,如果您有 1000 个不同的 String 对象,它们都具有相同的内容 "abc",JVM 可以使它们在内部共享相同的 char[]。但是,您仍然有 1000 个不同的 String 对象。

使用intern(),您将只有一个String 对象。因此,如果您关心节省内存,intern() 会更好。它将节省空间,以及 GC 时间。

不过,intern() 的表现不是很好,我上次听说的。拥有自己的字符串缓存可能会更好,即使使用 ConcurrentHashMap ... 但您需要对其进行基准测试以确保。

【讨论】:

  • 您是否知道其他差异?
  • 实际上,使用 String.intern 的性能与手动字符串池相当。 Mikhail Vorontsov 进行了一些性能基准测试,并表明在将 StringTableSize 参数设置为足够高到素数的情况下,性能与您自己手动进行字符串池相当。 http://java-performance.info/string-intern-in-java-6-7-8/
【解决方案2】:

作为评论参考,请参阅:http://java-performance.info/string-intern-in-java-6-7-8/。这是非常有见地的参考,我学到了很多,但我不确定它的结论是否一定是“一刀切”。每个方面都取决于您自己的应用程序的需求 - 强烈建议对实际输入数据进行测量!

主要因素可能取决于你能控制什么:

  • 您可以完全控制 GC 的选择吗?例如,在 GUI 应用程序中,仍然有充分的理由使用 Serial GC。 (该进程的总内存占用要低得多 - 考虑 400 MB 与约 1 GB 对于中等复杂的应用程序,并且更愿意释放内存,例如在使用量短暂飙升之后)。因此,您可以选择它或为您的用户提供选项。 (如果堆仍然很小,那么暂停应该不是什么大问题。

  • 您可以完全控制代码吗? G1GC 选项非常适合您无法编辑的 3rd 方库(和应用程序!)。

第二个考虑因素(根据@ZhongYu 的回答)是String.intern 可以对String 对象本身进行重复数据删除,而G1GC 必须只能对其私有char[] 字段进行重复数据删除。

第三个考虑因素可能是 CPU 使用率,例如您的用户是否关心对笔记本电脑电池寿命的影响。 G1GC 将运行一个额外的线程,专门用于对堆进行重复数据删除。例如,我用它来运行 Eclipse,发现它在启动后导致 CPU 活动的初始阶段增加(想想 1 - 2 分钟),但它停留在一个较小的堆“正在使用”并且没有明显(只是眼睛-球任务管理器)CPU开销或此后减慢。因此,我想一定百分比的 CPU 内核将用于重复数据删除(在?之后?)高内存流失期间。 (当然,如果你调用 String.intern everywhere 可能会有相当的开销,它也可以串行运行,但是...)

您可能不需要到处进行字符串重复数据删除。可能只有某些代码区域:

  • 真正影响长期堆使用,
  • 创建高比例的重复字符串

通过选择性地使用String.intern,代码的其他部分(可能会创建临时或半临时字符串)不会付出代价。

最后是 Guava 实用程序的快速插件:Interner,其中:

为其他不可变类型提供与 String.intern() 等效的行为

您也可以将它用于字符串。内存可能(并且应该是)您最关心的性能问题,因此这可能并不经常适用:但是,当您需要从某个热点区域挤出每一滴速度时,我的经验是基于 Java 的弱引用HashMap 解决方案的运行速度确实比 JVM 的 String.intern() 的 C++ 实现稍快但始终快,即使在调整了 jvm 选项之后也是如此。 (还有好处:您无需调整 JVM 选项以适应不同的输入。)

【讨论】:

    【解决方案3】:

    我想介绍另一个关于目标受众的决定因素:

    • 对于一个系统集成商来说,系统由许多不同的库/框架组成,影响这些库内部开发的能力很低,如果内存是一个问题,StringDeDuplication 可能是一个快速的赢家。它会影响 JVM 中的所有字符串,但 G1 只会使用空闲时间来做这件事。当使用另一个参数(StringDeduplicationAgeThreshold)计算重复数据删除时,您甚至可以进行调整
    • 对于分析自己的代码的开发人员,String.intern 可能更有趣。有必要对领域模型进行深入的审查,以决定是否调用实习生,以及何时调用。根据经验,当您知道 String 将包含一组有限的值时,您可以使用 intern,例如一种枚举集(即国家名称、月份、星期几...)。

    【讨论】:

      猜你喜欢
      • 2015-03-13
      • 1970-01-01
      • 2016-11-03
      • 2023-03-21
      • 2014-02-19
      • 2017-02-15
      • 2012-10-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多