Я исследую String.intern(), и этот метод снижает производительность. Я сравнил String.intern() с ConcurrentHashMap.putIfAbsent(s,s) с Microbenchmark. Используется 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));
}
}
Результат ожидаемый. ConcurrentHashMap быстрее, чем 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() медленнее, чем ConcurrentHashMap, потому что String.intern() является собственной реализацией HashTable. А затем прочитайте javadoc о HashTable, в этой документации говорится:
If a thread-safe implementation is not needed, it is recommended to use HashMap in place of Hashtable. If a thread-safe highly-concurrent implementation is desired, then it is recommended to use ConcurrentHashMap in place of Hashtable.
Это очень запутанная ситуация. Он рекомендует ConcurrentHashMap, но использует HashTable, несмотря на снижение производительности. Кто-нибудь знает, почему используется собственный экземпляр реализации HashTable для ConcurrentHashMap?




Здесь происходит ряд вещей:
Ваши тесты имеют очень большие полосы погрешностей. Количество повторов, вероятно, слишком мало. Это делает результаты под вопросом.
Не похоже, чтобы ваши тесты сбрасывали кеши «интернированных строк» после каждого запуска1. Это означает, что кэши растут, и каждое повторение будет начинаться с разных условий. Это может объяснить полосы ошибок ...
Ваш ConcurrentHashMap функционально не эквивалентен String::intern. Последний использует собственный эквивалент объектов Reference, чтобы гарантировать, что интернированные строки могут быть удалены сборщиком мусора. Ваша реализация ConcurrentHashMap не работает. Почему это важно?
ConcurrentHashMap — это массовая утечка памяти.String.intern() slower than ConcurrentHashMap because String.intern() is native HashTable implementation.
Нет. Настоящая причина в том, что нативная реализация работает по-другому:
intern) пул строк использует пользовательскую хеш-таблицу, реализованную в собственном коде.Обратите внимание, что эти вещи значительно различаются в разных версиях Java.
This is very confusing situation. It recommend ConcurrentHashMap, but it using HashTable although performance penalty.
Теперь вы говорите о другом сценарии, который не имеет отношения к тому, что вы делаете.
Обратите внимание, что String::intern не использует ни HashTable, ни HashMap; см. выше.
Цитата, которую вы нашли, о том, как получить хорошую производительность одновременный из хеш-таблицы. Ваш тест (AFAIK) однопоточный. Для последовательного варианта использования HashMap даст лучшую производительность, чем другие.
Does anyone have any idea about why used native
HashTableimplementation instance ofConcurrentHashMap?
Он не использует хеш-таблицу; см. выше. Есть ряд причин, по которым это не HashTable или HashMap или ConcurrentHashMap:
Reference значительны.Наконец, будьте осторожны, чтобы не сосредоточиться не на той проблеме. Если вы пытаетесь оптимизировать стажировку, потому что она является узким местом в вашем приложении, другая стратегия — вообще не стажироваться. На практике это редко экономило память (особенно по сравнению с дедупликацией строк в G1GC) и редко улучшало производительность обработки строк.
В итоге:
String::intern не оптимизирован исключительно (даже в первую очередь) для скорости.1 - And in the native intern case, I don't think that is possible.
2 - A Java memory leak in the regular heap impacts on long-term GC performance because the retained objects need to be repeatedly marked and copied by the GC. There may be secondary effects too.
String.internиспользует хеш-таблица (структуру), но неjava.util.Hashtableкласс.