指令重排序,內存模型排序規則,內存屏障 JVM內存模型、指令重排、內存屏障

JVM內存模型、指令重排、內存屏障 概念解析html

1,指令重排序

大多數現代微處理器都會採用將指令亂序執行out-of-order execution,簡稱OoOE或OOE)的方法,
條件容許的狀況下,直接運行當前有能力當即執行的後續指令避開獲取下一條指令所需數據時形成的等待
經過亂序執行的技術,處理器能夠大大提升執行效率。 除了處理器,常見的Java運行時環境的JIT編譯器也會作指令重排序操做,即生成的機器指令與字節碼指令順序不一致。

2,as-if-serial語義

As-if-serial語義的意思是,全部的動做(Action)均可覺得了優化而被重排序,可是必須保證它們重排序後的結果和程序代碼自己的應有結果是一致的
Java編譯器運行時處理器都會保證單線程下的as-if-serial語義

ps:即指令好像是連續的,是對這種執行效果特性的一個說法。java

爲了保證這一語義,重排序不會發生在有數據依賴的操做之中緩存

3,內存訪問重排序與內存可見性

計算機系統中,爲了儘量地避免處理器訪問主內存的時間開銷,處理器大多會利用緩存(cache)以提升性能。
即緩存中的數據與主內存的數據並非實時同步的,各CPU(或CPU核心)間緩存的數據也不是實時同步的。
這致使在同一個時間點,各CPU所看到同一內存地址的數據的值多是不一致的從程序的視角來看,就是在同一個時間點,各個線程所看到的共享變量的值可能是不一致的。

有的觀點會將這種現象也視爲重排序的一種,命名爲「內存系統重排序」。
由於這種內存可見性問題形成的結果就好像是內存訪問指令發生了重排序同樣。
執行了殊不知道執行了覺得執行了卻重排序沒有執行形成相同效果

4,內存訪問重排序與Java內存模型

Java的目標是成爲一門平臺無關性的語言,即Write once, run anywhere. 可是不一樣硬件環境下指令重排序的規則不盡相同。
例如,x86下運行正常的Java程序在IA64下就可能獲得非預期的運行結果。

爲此,JSR-1337制定了Java內存模型(Java Memory Model, JMM),旨在提供一個統一的可參考的規範屏蔽平臺差別性從Java 5開始,Java內存模型成爲Java語言規範的一部分

根據Java內存模型中的規定,能夠總結出如下幾條happens-before規則。app

ps:內存模型即經過運行環境把一些可見性和重排序問題統一成一個標準描述函數

Happens-before的先後兩個操做不會被重排序且後者對前者的內存可見。post

程序次序法則:     線程中的每一個動做A都happens-before於該線程中的每個動做B,其中,在程序中,全部的動做B都能出如今A以後。
監視器鎖法則:     對一個監視器鎖的解鎖 happens-before於每個後續對同一監視器鎖的加鎖。
volatile變量法則:對volatile域的寫入操做happens-before於每個後續對同一個域的讀寫操做。
線程啓動法則:     在一個線程裏,對Thread.start的調用會happens-before於每一個啓動線程的動做。
線程終結法則:線程中的任何動做都happens-before於其餘線程檢測到這個線程已經終結、或者從Thread.join調用中成功返回,或Thread.isAlive返回false。
中斷法則:        一個線程調用另外一個線程的interrupt happens-before於被中斷的線程發現中斷。
終結法則:        一個對象的構造函數的結束happens-before於這個對象finalizer的開始。
傳遞性:          若是A happens-before於B,且B happens-before於C,則A happens-before於C

Happens-before關係只是對Java內存模型的一種近似性的描述,它並不夠嚴謹,但便於平常程序開發參考使用,性能

關於更嚴謹的Java內存模型的定義和描述,請閱讀JSR-133原文或Java語言規範章節17.4。優化

除此以外,Java內存模型對volatile和final的語義作了擴展。this

對volatile語義的擴展保證了volatile變量在一些狀況下不會重排序,volatile的64位變量double和long的讀取和賦值操做都是原子的對final語義的擴展保證一個對象的構建方法結束前,全部final成員變量都必須完成初始化(前提是沒有this引用溢出)。

(ps:沒有理解final的意思)url

Java內存模型關於重排序的規定,總結後以下表所示。(ps:下表沒看懂)

5,內存屏障

內存屏障(Memory Barrier,或有時叫作內存柵欄,Memory Fence)是一種CPU指令,用於控制特定條件下的重排序和內存可見性問題

Java編譯器也會根據內存屏障的規則禁止重排序。

內存屏障能夠被分爲如下幾種類型:

LoadLoad  屏障:對於這樣的語句Load1; LoadLoad; Load2,在Load2及後續讀取操做要讀取的數據被訪問前,保證Load1要讀取的數據被讀取完畢StoreStore屏障:對於這樣的語句Store1; StoreStore; Store2,在Store2及後續寫入操做執行前,保證Store1的寫入操做對其它處理器可見LoadStore 屏障:對於這樣的語句Load1; LoadStore; Store2,在Store2及後續寫入操做被刷出前,保證Load1要讀取的數據被讀取完畢
StoreLoad 屏障:對於這樣的語句Store1; StoreLoad; Load2,在Load2及後續全部讀取操做執行前,保證Store1的寫入對全部處理器可見
它的開銷是四種屏障中最大的。在大多數處理器的實現中,這個屏障是個萬能屏障,兼具其它三種內存屏障的功能

有的處理器的重排序規則較嚴,無需內存屏障也能很好的工做Java編譯器會在這種狀況下不放置內存屏障
爲了實現上一章中討論的JSR-133的規定,Java編譯器會這樣使用內存屏障。(ps:下表沒看懂)

相關文章
相關標籤/搜索