Java 8字符串去重与String.intern()函数的区别

20

我正在阅读关于Java 8更新20中有关字符串去重功能的内容(更多信息),但我不确定这是否基本上使得String.intern()过时。

我知道这个JVM功能需要G1垃圾回收器,这对许多人来说可能不是一个选择,但假设使用G1GC,JVM自动去重和手动使用intern()你的字符串之间是否存在任何差异/优劣势 (一个明显的优势是不必在代码中污染调用intern())。

考虑到Oracle可能会在Java 9中将G1GC作为默认GC,这尤其有趣。


4
无论如何,结论总是一样:你不应该在意。 - fge
抱歉,不应该关心什么?是关于使用哪个(意味着它们是等价的)还是关于新功能(意味着它并不那么有用)? - Hilikus
2
意思是:毫不犹豫地使用String类。 - fge
1
@DavidConrad 我的应用程序中都没有使用这两个功能。我只是想了解这个功能是如何工作的。你有什么见解,为什么我不必担心它?每个人都只是说不用担心,但没有解释。你是在暗示字符串去重并不是非常有用/有效吗?之前关于只考虑使用字符串而不考虑所有这些的评论与启用JVM中的功能无关(这不是开发时更改)。它更多地涉及调整运行时。 - Hilikus
1
@DavidConrad 在完整的引用中,Knuth实际上量化了他的建议:“我们应该忘记小的效率问题,大约有97%的时间。过早的优化是万恶之源”。因此,Knuth确实关心低级性能(非常关心-请参见《编程艺术》中的详细信息),以及将受益于一些关注和调整的3%代码(整个应用程序)。但是,是的,我们要牢记:优先考虑自己的时间,而不是CPU时间。 :-) - Luke Usherwood
显示剩余4条评论
3个回答

13

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

使用intern(),你只会有一个String对象。因此,如果节省内存是你的关注点,intern()会更好。它可以节省空间,以及垃圾回收时间。

然而,intern()的性能并不太好,上次我听说是这样的。你最好自己建立字符串缓存,甚至使用ConcurrentHashMap...但你需要对其进行基准测试以确保。


你知道还有其他的区别吗? - Hilikus
4
实际上,使用String.intern方法的性能与手动字符串池化的性能相当。Mikhail Vorontsov进行了一些性能基准测试,并显示,将StringTableSize参数设置足够高到素数时,性能与手动字符串池化相当。[http://java-performance.info/string-intern-in-java-6-7-8/] - Thad Guidry

6
作为评论参考,请查看:http://java-performance.info/string-intern-in-java-6-7-8/。这是非常有见地的参考资料,我学到了很多,但我不确定它的结论是否一定适用于所有情况。每个方面都取决于您自己应用程序的需求-强烈建议对现实输入数据进行测量!可能的主要因素取决于您掌控的内容:
- 您是否完全控制GC的选择?例如,在GUI应用程序中,仍然有充分的理由使用Serial GC(进程的总内存占用更低-考虑到中等复杂度的应用程序的400 MB与约1 GB,并且更愿意释放内存,例如在瞬态使用量增加后)。因此,您可以选择它或为用户提供选项。(如果堆保持较小,则暂停不会成为大问题)。 - 您是否完全控制代码? G1GC选项非常适用于您无法编辑的第三方库(和应用程序!)。
第二个考虑因素(根据@ZhongYu的答案)是,String.intern可以去重String对象本身,而G1GC必须去重它们的私有char []字段。第三个考虑因素可能是CPU使用率,例如如果影响笔记本电池寿命可能会引起用户关注。 G1GC将运行一个专用线程来去重堆。例如,我尝试使用它来运行Eclipse,并发现它在启动后导致了一段增加的CPU活动时间(大约1-2分钟),但它在较小的“正在使用”的堆上稳定下来,没有明显的(只是通过眼球观察任务管理器)CPU开销或减速。因此,我想象一个CPU核心的一定百分比将被用于去重(在?之后?)高内存翻转期间。(当然,如果您到处调用String.intern,则可能存在可比较的开销,这也是串行运行的,但是...)
您可能不需要在任何地方进行字符串去重。可能只有某些代码区域:
- 真正影响长期堆使用率,并且 - 创建高比例重复字符串
通过有选择地使用String.intern,其他部分的代码(可能创建临时或半临时字符串)不需要付出代价。
最后,快速推广Guava实用程序:Interner,它提供了与其他不变类型的String.intern()等效的行为。

你也可以用它来操作字符串。内存可能是(并且应该是)你最关心的性能问题,所以这种情况可能不经常出现:然而当你需要从某些热点区域挤出每一滴速度时,我的经验是基于Java的弱引用哈希映射解决方案比JVM的C++实现String.intern()运行略快但始终如一,即使调整了jvm选项。(另外:你不需要调整JVM选项来处理不同输入的规模。)


3

我想介绍另一个与目标受众相关的决策因素:

  • 对于一个系统集成商而言,如果其系统由许多不同的库/框架组成,并且很难影响这些库的内部开发,如果内存是一个问题,那么StringDeDuplication可能会是一个快速解决方案。它将影响JVM中的所有字符串,但G1只会利用空闲时间来进行去重。您甚至可以通过使用另一个参数(StringDeduplicationAgeThreshold)来调整何时计算去重。
  • 对于一位开发人员来说,如果他正在对自己的代码进行分析,那么String.intern可能更有趣。需要仔细审查领域模型才能决定是否调用intern以及何时调用。作为经验法则,当您知道字符串将包含一组有限的值(如国家名称、月份、星期几等)时,可以使用intern。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接