我瞭解僞分享是在看Disruptor源碼時開始的。html
JDK8中引入了@Contented,不過這個註解在sun包中,以下List-1java
List-1數組
package sun.misc; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.TYPE}) public @interface Contended { String value() default ""; }
這個註解只能在類屬性、類上使用。緩存
下面的Centos版本是Centos7 64位,運行在個人虛擬機上,可用的cpu核數是4,可用的運行內存是8192M,即8G。bash
爲了高效地存取緩存, 不是簡單隨意地將單條數據寫入緩存的. 緩存是由緩存行組成的, 典型的一行是64字節,CentOS上能夠查看到,以下:架構
List-2oracle
[dmj@localhost ~]$ cat /sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size 64
來看下cache/index3到cache/index0的size變化,以下:jvm
List-3 從index3到index0是逐漸變小(index0表示越接近CPU,index3表示離CPU越遠)性能
[dmj@localhost ~]$ cat /sys/devices/system/cpu/cpu0/cache/index3/size 8192K [dmj@localhost ~]$ cat /sys/devices/system/cpu/cpu0/cache/index2/size 256K [dmj@localhost ~]$ cat /sys/devices/system/cpu/cpu0/cache/index1/size 32K [dmj@localhost ~]$ cat /sys/devices/system/cpu/cpu0/cache/index0/size 32K
來看下cache/index0到cache/index3的level變化,以下:測試
List-4 index3的level大於index0的
[dmj@localhost ~]$ cat /sys/devices/system/cpu/cpu0/cache/index0/level 1 [dmj@localhost ~]$ cat /sys/devices/system/cpu/cpu0/cache/index1/level 1 [dmj@localhost ~]$ cat /sys/devices/system/cpu/cpu0/cache/index2/level 2 [dmj@localhost ~]$ cat /sys/devices/system/cpu/cpu0/cache/index3/level 3
來看下cache/index0到cache/index3的coherency_line_size值變化
List-5 值都是64
[dmj@localhost ~]$ cat /sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size 64 [dmj@localhost ~]$ cat /sys/devices/system/cpu/cpu0/cache/index1/coherency_line_size 64 [dmj@localhost ~]$ cat /sys/devices/system/cpu/cpu0/cache/index2/coherency_line_size 64 [dmj@localhost ~]$ cat /sys/devices/system/cpu/cpu0/cache/index3/coherency_line_size 64
來看下cache/index0到cache/index3的type
List-6 index0是Data、index1是Instruction、index2和index3都是Unified
[dmj@localhost ~]$ cat /sys/devices/system/cpu/cpu0/cache/index0/type Data [dmj@localhost ~]$ cat /sys/devices/system/cpu/cpu0/cache/index1/type Instruction [dmj@localhost ~]$ cat /sys/devices/system/cpu/cpu0/cache/index2/type Unified [dmj@localhost ~]$ cat /sys/devices/system/cpu/cpu0/cache/index3/type Unified
根據上面的數據,我猜想個人CentOS7上的CPU和緩存架構以下:
圖1 猜想的我Centos上的CPU、緩存結構圖
注:圖1僅僅是我猜想,極可能是錯誤的,建議讀者本身去查看本身Linux系統中上述的值。
mac系統查看"L1 data cache line size"的命令以下,參考自google論壇:
List-7 我mac系統上,獲得的值也是64
mjduan@mjduandeMacBook-Pro:/tmp % sysctl hw.cachelinesize hw.cachelinesize: 64
CentOS上查看"L1 data cache line size"的命令以下,參考自google論壇:
List-8 我CentOS上,獲得的值也是64
[dmj@localhost ~]$ getconf LEVEL1_DCACHE_LINESIZE 64
上面作的基本是鋪墊,由咱們本身驗證得緩存行是64bytes,怎麼肯定單位是bytes,參考國外博客,以後咱們進一步進入僞共享。我發現這篇51CTO博客講的挺好的,這裏就再也不重複描述。關鍵點在於:假設倆個獨立的變量x、y內存上位於同一個緩存行中,CPU0要對x進行操做,CPU1要對y進行操做。當CPU0加載換成到本身的L0緩存中,CPU1加載緩存行到本身的L0緩存中,以後CPU0修改本身L0緩存中的x,此時CPU1的L0緩存中的緩存行是失效了,因此此時CPU1不能對本身緩存行進行操做,CPU1不得不從新從L3或者主存加載該緩存行。因此CPU0和CPU1得不到並行執行。
通過上述討論,引出一個問題,CPU1的L0中緩存行怎麼知道,CPU0該緩存行中的某些數據,這個要了解下MESI協議,能夠了解下這篇博客。
"對於HotSpot JVM,全部對象都有兩個字長的對象頭。第一個字是由24位哈希碼和8位標誌位(如鎖的狀態或做爲鎖對象)組成的Mark Word。第二個字是對象所屬類的引用。若是是數組對象還須要一個額外的字來存儲數組的長度。每一個對象的起始地址都對齊於8字節以提升性能",這段話來自於51CTO開發頻道博客。這讓我想起來《深刻理解計算機》這本書中講過變量聲明順序對內存的優化。
一般下給出的是填充緩存行,以下List-9。
List-9 "public long p1, p2, p3, p4, p5, p6; // comment out"這行就是填充使用的
public final static class VolatileLong { public volatile long value = 0L; public long p1, p2, p3, p4, p5, p6; // comment out }
可是這個要求開發人員對計算機底層和JVM內存有深的瞭解,要優化,可是也要慎重:
我的主觀的認爲,沒有被使用的局部變量頗有可能被優化,可是沒有被使用到的類屬性被優化的可能性不是很大,注意,這個僅僅是主觀的認爲,沒有試驗驗證過。
JDK中的@Contented註解,這個的做用我沒有仔細驗證過,且這個註解是在sun.misc包中的,這個在開頭給出了。Oracle的博客給出說這個註解能夠必定程度上減小False sharing的發生。
注意通過本人驗證,JDK8上加上@Contented註解是不會生效的,除非加上List-10中的JVM參數。我是怎麼知道不加List-10的參數,@Contented不會生效的呢,請參考這裏,連接裏面的是個OpenJDK jol的例子,在運行是測試下加上和不加上List-10JVM參數的結果,運行結果能夠看到佔用內存bits位數的變化。
List-10
-XX:-RestrictContended
除了填充和@Contented外,頗有可能有其它方法,建議讀者多google/bing.com,也許能找到更充分的證據,最後是要本身驗證。
最好是本身測試下,若是測試發現優化後確實效率高了,能夠運行在生產上,特別是注意測試數據量大的狀況和長時間運行的狀況。