我正在研究String. intern(),这个方法有一个性能损失。我已经将String.intern()与Concurrent tHashMap.putIfAbsend(s,s)与微基准进行了比较。使用Java1.8。0_212,Ubuntu 18.04.2 LTS
@Param({"1", "100", "10000", "1000000"})
private int size;
private StringIntern stringIntern;
private ConcurrentHashMapIntern concurrentHashMapIntern;
@Setup
public void setup(){
stringIntern = new StringIntern();
concurrentHashMapIntern = new ConcurrentHashMapIntern();
}
public static class StringIntern{
public String intern(String s){
return s.intern();
}
}
public static class ConcurrentHashMapIntern{
private final Map<String, String> map;
public ConcurrentHashMapIntern(){
map= new ConcurrentHashMap<>();
}
public String intern(String s){
String existString = map.putIfAbsent(s, s);
return (existString == null) ? s : existString;
}
}
@Benchmark
public void intern(Blackhole blackhole){
for(int count =0; count<size; count ++){
blackhole.consume(stringIntern.intern("Example "+count));
}
}
@Benchmark
public void concurrentHashMapIntern(Blackhole blackhole){
for(int count =0; count<size; count++){
blackhole.consume(concurrentHashMapIntern.intern("Example " +count));
}
}
结果符合预期。搜索字符串时,Concurrent tHashMap比String. intern()更快。
Benchmark (size) Mode Cnt Score Error Units
MyBenchmark.concurrentHashMapIntern 1 avgt 5 0.056 ± 0.007 us/op
MyBenchmark.concurrentHashMapIntern 100 avgt 5 6.094 ± 2.359 us/op
MyBenchmark.concurrentHashMapIntern 10000 avgt 5 787.802 ± 264.179 us/op
MyBenchmark.concurrentHashMapIntern 1000000 avgt 5 136504.010 ± 17872.866 us/op
MyBenchmark.intern 1 avgt 5 0.129 ± 0.007 us/op
MyBenchmark.intern 100 avgt 5 13.700 ± 2.404 us/op
MyBenchmark.intern 10000 avgt 5 1618.514 ± 460.563 us/op
MyBenchmark.intern 1000000 avgt 5 1027915.854 ± 638910.023 us/op
String. intern()比ConloctHashMap慢,因为String.intern()是原生HashTable实现。然后,阅读有关HashTable的javadoc,该文档说:
如果不需要线程安全的实现,建议使用HashMap代替Hashtable。如果需要线程安全的高并发实现,那么建议使用ConloctHashMap代替Hashtable。
这是一个非常令人困惑的情况。它推荐ConloctHashMap,但它使用HashTable,尽管性能下降。有人知道为什么使用本地HashTable植入ConloctHashMap实例吗?
这里发生了许多事情:
>
您的基准测试有非常大的误差条。重复计数可能太小。这使得结果值得怀疑。
每次运行1后,您的基准测试似乎并没有重置“实习字符串”缓存。这意味着缓存正在增长,每次重复都将从不同的条件开始。这可以解释错误条…
您的ConCurrentHashMap
在功能上不等同于String::intern
。后者使用与引用
对象等效的本机对象来确保可以垃圾收集被拘留的字符串。您的ConCurrentHashMap
实现没有。为什么这很重要?
String. intern()比并发HashMap慢,因为String.intern()是原生HashTable实现。
不。真正的原因是本机实现的做法不同:
intern
)字符串池使用本机代码实现的自定义哈希表。请注意,这些东西在不同的Java版本中差异很大。
这是非常令人困惑的情况。它建议使用并发HashMap,但它使用HashTable虽然性能下降。
现在你在谈论一个不同的场景,这与你正在做的事情无关。
>
注意String::实习生
不使用HashTable
或HashMap
;见上文。
您找到的引用是关于如何从哈希表中获得良好的并发性能。您的基准是(AFAIK)单线程。对于串行用例,HashMap
将提供比其他人更好的性能。
有没有人知道为什么使用并发HashMap
的本机HashTable
实现实例?
它不使用哈希表;见上文。它没有HashTable
或HashMap
或ConCurrentHashMap
的原因有很多:
参考
类的内存和CPU开销很大。最后,要小心你没有关注这里的错误问题。如果你试图优化实习生,因为这是你应用程序的瓶颈,另一个策略是完全不实习。在实践中,它很少节省内存(尤其是与G1GC的字符串去重相比),也很少提高字符串处理性能。
综上所述:
String::intern
不仅仅(甚至主要)针对速度进行了优化。
1-在nativeintern
的情况下,我认为这是不可能的。
2-常规堆中的Java内存泄漏会影响长期GC性能,因为保留的对象需要被GC重复标记和复制。也可能有次要影响。