我自己做了一些基准测试。对于搜索成本部分,我决定将 String.intern() 与 ConcurrentHashMap.putIfAbsent(s,s) 进行比较。基本上,这两个方法做同样的事情,除了 String.intern() 是一个本地方法,它存储和读取直接在 JVM 中管理的 SymbolTable,而 ConcurrentHashMap.putIfAbsent() 只是一个普通的实例方法。
您可以在github gist上找到基准代码(因为没有更好的放置位置)。您还可以在源文件顶部的注释中找到我在启动 JVM 时使用的选项(以验证基准没有倾斜)。
无论如何,这是结果:
搜索成本(单线程)
传奇
- count:我们试图汇集的不同字符串的数量
- 初始实习生:在字符串池中插入所有字符串所需的时间(以毫秒为单位)
- 查找相同的字符串:使用与先前在池中输入的完全相同的实例从池中再次查找每个字符串所需的时间(以毫秒为单位)
- 查找相等字符串:从池中再次查找每个字符串所需的时间(以毫秒为单位),但使用不同的实例
String.intern()
count initial intern lookup same string lookup equal string
1'000'000 40206 34698 35000
400'000 5198 4481 4477
200'000 955 828 803
100'000 234 215 220
80'000 110 94 99
40'000 52 30 32
20'000 20 10 13
10'000 7 5 7
ConcurrentHashMap.putIfAbsent()
count initial intern lookup same string lookup equal string
1'000'000 411 246 309
800'000 352 194 229
400'000 162 95 114
200'000 78 50 55
100'000 41 28 28
80'000 31 23 22
40'000 20 14 16
20'000 12 6 7
10'000 9 5 3
搜索成本的结论:String.intern() 调用起来非常昂贵。它的扩展性非常糟糕,在 O(n) 中,其中 n 是池中的字符串数。当池中的字符串数量增加时,从池中查找一个字符串的时间会增加很多(对于 10'000 个字符串,每次查找 0.7 微秒,对于 1'000'000 个字符串,每次查找需要 40 微秒)。
ConcurrentHashMap 按预期缩放,池中的字符串数量对查找速度没有影响。
基于这个实验,我强烈建议避免使用 String.intern() 如果你要实习的字符串不止几个。