簡體   English   中英

Java String.intern()使用HashTable而不是ConcurrentHashMap

[英]Java String.intern() use HashTable instead of ConcurrentHashMap

我研究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實現。 然后,閱讀關於HashTable的javadoc ,這個文檔說:

如果不需要線程安全實現,建議使用HashMap代替Hashtable。 如果需要線程安全的高度並發實現,那么建議使用ConcurrentHashMap代替Hashtable。

這是非常令人困惑的情況。 它推薦使用ConcurrentHashMap,但它使用HashTable雖然性能下降。 有沒有人知道為什么使用ConcurrentHashMap的本機HashTable實現實例?

這里有很多事情要做:

  1. 您的基准測試具有非常大的誤差線。 重復計數可能太小了。 這使得結果有問題

  2. 看起來你的基准測試不會在每次運行1之后重置“interned string”緩存。 這意味着緩存正在增長,每次重復都將以不同的條件開始。 這可以解釋錯誤欄...

  3. 您的ConcurrentHashMap在功能上與String::intern 后者使用與Reference對象相當的本機,以確保可以對已中斷的字符串進行垃圾回收。 你的ConcurrentHashMap實現沒有。 為什么這很重要?

    • 你的ConcurrentHashMap是一個巨大的內存泄漏。
    • 在GC時間,參考機制很昂貴。

String.intern()比ConcurrentHashMap慢,因為String.intern()是本機HashTable實現。

不是。真正的原因是本機實現的方式不同:

  • 調用String::intern時可能會有JNI調用開銷。
  • 內部表示是不同的。
  • 它必須處理影響GC性能的參考。
  • 還有與字符串重復刪除和其他事情的幕后交互。

請注意,不同Java版本的這些內容差異很大。

這是非常令人困惑的情況。 它推薦使用ConcurrentHashMap,但它使用HashTable雖然性能下降。

現在你談論的是一個與你正在做的事情無關的不同場景。

  • 請注意, String::intern不使用HashTableHashMap ; 往上看。

  • 您找到的引用是關於如何從哈希表中獲得良好的並發性能。 您的基准是(AFAIK)單線程。 對於串行用例, HashMap將提供比其他用戶更好的性能。

有沒有人知道為什么使用ConcurrentHashMap的本機HashTable實現實例?

它不使用哈希表; 往上看。 有很多原因它不是HashTableHashMapConcurrentHashMap

  • 它正在更加關注內存利用率。 所有Java哈希表實現都是內存飢餓 ,這使得它們不適合通用字符串實習。
  • 使用Reference類的內存和CPU開銷很重要。
  • 計算新創建的長度為N的字符串的散列是O(N),當實際上可能是數百/數千個字符長的字符串時,這將是重要的。

最后,要小心,你沒有關注這里的錯誤問題。 如果您正在嘗試優化實習,因為它是您的應用程序的瓶頸,另一個策略是根本不實習。 實際上,它很少保存內存(特別是與G1GC的字符串重復數據刪除相比)並且很少提高字符串處理性能。


綜上所述:

  • 你正在比較蘋果和橘子。 您的基於地圖的實現不等同於本機實習。
  • String::intern並未針對速度進行單獨(甚至主要)優化。
  • 通過關注速度,您忽略了內存利用率......以及內存利用率對速度的次要影響。
  • 考慮一下根本不實習的潛在優化。

1 - 在本土intern案件中,我認為這是不可能的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM