JVM偽共享

banq發表於2011-09-01
偽共享False sharing說明JVM底層技術也不讓人那麼放心。

記憶體快取系統中基本單元是快取記憶體行(Cache lines). cpu會把資料從記憶體載入到快取記憶體中 ,這樣可以獲得更好的效能,快取記憶體預設大小是64 Byte為一個區域,一個區域在一個時間點只允許一個核心操作,也就是說不能有多個核心同時操作一個快取區域。

因為快取記憶體是64位元組,而Hotspot JVM的物件頭是兩個部分組成,第一部分是由24位元組的hash code和8位元組的鎖等狀態標識組成,第二部分是指向該物件類的引用。基本型別位元組如下:
doubles (8) and longs (8)
ints (4) and floats (4)
shorts (2) and chars (2)
booleans (1) and bytes (1)
references (4/8)

因此,一個快取記憶體64位元組可以放下多個欄位,如果這多個欄位位於同一個快取記憶體區,雖然它們是類的不同欄位,如下程式碼:

Class A{
   int x;
   int y;
}
<p class="indent">

x和y被放在同一個快取記憶體區,如果一個執行緒修改x;那麼另外一個執行緒修改y,必須等待x修改完成後才能實施。

雖然兩個執行緒修改各種獨立變數,但是因為這些獨立變數被放在同一個快取記憶體區,效能就影響了。測試結果如後面。

當多核CPU執行緒同時修改在同一個快取記憶體行各自獨立的變數時,會不自不覺地影響效能,這就發生了偽共享False sharing,偽共享是效能的無聲殺手。

解決方便是將快取記憶體剩餘的位元組填充填滿(pad),確保不發生多個欄位被擠入一個快取記憶體區,下面測試結果圖就是和填充後效能比較。

實現位元組填充的框架有 Disruptor,在RingBuffer中實現填充。關於Disruptor可見infoQ這個影片,用1毫秒的延時得到100K+ TPS吞吐量,JDK的ArrayQueue並行環境不見得是最快的,該影片後面討論很多,讓人大跌眼鏡啊,開放原始碼多有好處啊,別人能發現你不能發現的漏洞。另外一篇解剖Disruptor


C也有類似偽共享發生,見這裡

[該貼被banq於2011-09-01 09:21修改過]

相關文章