Java 8 字符串重复数据删除与 String.intern()

Java 8 String deduplication vs. String.intern()

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

我知道此 JVM 功能需要 G1 垃圾收集器,这对许多人来说可能不是一个选项,但假设有人使用 G1GC,是否有任何 difference/advantage/disadvantage 自动重复数据删除由 JVM 完成 vs 手动必须 intern 你的字符串 (一个明显的优点是不必通过调用 intern() 污染你的代码)?

考虑到 Oracle 可能会在 java 9

中将 G1GC 设为默认 GC,这尤其有趣

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

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

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

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

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

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

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

  • 您对 GC 的选择有完全的控制权吗?例如,在 GUI 应用程序中,仍然有充分的理由使用串行 GC。 (该进程的总内存占用量要低得多——对于一个中等复杂的应用程序来说,400 MB vs ~1 GB,并且更愿意释放内存,例如在使用量出现短暂峰值之后)。所以你可以选择那个或者给你的用户选择。 (如果堆仍然很小,暂停应该不是什么大问题)。

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

第二个考虑(根据@ZhongYu的回答)是String.intern可以去重String对象本身,而G1GC必然只能去重它们的私有char[]领域。

第三个考虑因素可能是 CPU 使用情况,比如您的用户是否关心对笔记本电脑电池寿命的影响。 G1GC 将 运行 一个额外的线程专用于对堆进行去重。例如,我在 运行 Eclipse 上玩这个,发现它在启动后导致 CPU activity 的初始阶段增加(想想 1 - 2 分钟),但它稳定在一个较小的堆上"in-use" 并且没有明显的(只是观察任务管理器)CPU 开销或此后减速。所以我想一定百分比的 CPU 内核将用于重复数据删除(期间?之后?)高内存流失期间。 (当然,如果你在任何地方调用 String.intern 可能会有相当的开销,这也将是串行的 运行s,但是......)

您可能不需要在任何地方都进行字符串重复数据删除。可能只有某些代码区域:

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

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

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

Provides equivalent behavior to String.intern() for other immutable types

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