Java基礎:記憶體模型

weixin_33670713發表於2017-04-10

1. 引言

考慮到計算機組成的內容:

原始的計算機是CPU用於計算+硬碟用於儲存,由於CPU的高速發展和硬碟的緩慢發展,高速的儲存需要持續供電且價格昂貴,於是引入了由高速儲存組成的記憶體作為中間的緩衝層。形成了CPU-RAM-Main Memory的金字塔結構。

接下來,由於CPU的繼續發展,記憶體也漸漸跟不上CPU的速度,於是引入了更小更高速的cache作為CPU和記憶體的緩衝。形成了我們現在熟悉的計算機組成金字塔結構。

然後,由於CPU從單核發展成了多核,計算機從單處理器發展為多處理器,而每個處理器都有自己的cache,而這些快取記憶體又要共享同一個主存。為了保證多個快取中的資料一致性,誕生了很多協議,形成了如下結構。

4033480-669270a5cb33daa4.png

2. Java記憶體模型

4033480-fd15c49e4a56f13b.jpg

此時,執行緒引擎控制著多個執行緒,就如同實際計算機的多個CPU一樣,每個執行緒有自己的工作記憶體,而他們最終共享同一個主存。

這裡的主存,工作記憶體與JVM中的堆,棧,方法區不是同一層次記憶體劃分。

3. 記憶體間的互動操作

Java記憶體模型定義了八種操作來完成記憶體與工作記憶體的具體互動協議:

  • lock 鎖定,作用於主記憶體的變數,把一個變數標識為一個執行緒獨佔狀態。
  • unlock 解鎖,作用於主記憶體的變數,把一個處於鎖定狀態的變數釋放出來,釋放後的變數才可以被其他執行緒鎖定。
  • read 讀取,作用於主記憶體的變數,把一個變數從主記憶體傳入現成的工作記憶體中。
  • load 載入,作用於工作記憶體的變數,把通過read從主記憶體中得到的變數放入工作記憶體的變數副本中。
  • use 使用,作用與工作記憶體的變數,把工作記憶體中一個變數傳遞給執行引擎,每當虛擬機器遇到一個需要使用變數的值的位元組碼指令時將會執行這個動作。
  • assign 賦值,作用於工作記憶體的變數,把執行引擎中獲得的值賦給工作記憶體中的一個變數,每當虛擬機器遇到一個給變數賦值的位元組碼指令時會執行這個動作。
  • store 儲存,作用於工作記憶體的變數,把工作記憶體中的一個變數值傳到主內寸中。
  • write 寫入,作用於主記憶體的變數,把通過store從工作記憶體中得到的變數的值傳送到主記憶體的變數中。

用流程圖表示互動操作如下:

4033480-1306466469efe9c1.png

如果把一個變數從主存複製到工作記憶體,就需要按順序的執行read和load操作。

如果把一個變數從工作記憶體複製到主存,就需要按順序的執行store和write操作。

Java記憶體只要求必須按上述順序執行操作,沒有要求保證操作連續。

Java還規定上述8種操作必須符合如下七條規定:

  1. 不允許read-load,store-write這兩對操作的操作之一單獨出現。
  2. 不允許一個執行緒丟棄他的最近assign操作,即一個工作記憶體中的最終變數必須同步到主記憶體中。
  3. 在沒有發生任何assign操作時,不允許一個執行緒把工作記憶體中的變數同步到主記憶體中。(不允許無原因同步)
  4. 一個新變數只能在主記憶體中產生,不允許工作記憶體直接使用一個未被load或者assign的變數。換言之,執行use前必須load,執行store前必須assign。
  5. 一個變數同時只允許一個執行緒對其執行lock操作,lock和unlock必須成對出現。
  6. 如果一個變數事先沒有被執行lock操作,則不能執行unlock操作,也不允許去unlock其他執行緒的lock操作。
  7. 一個變數執行unlock前,必須把此變數同步到主記憶體中,即執行store和write操作。

四. 重排序

由上知八種操作沒有連續性要求,但是很多操作是其他操作的前提,操作間滿足一定的先序排列。因此,在編譯器對程式碼優化時,往往會通過重排序來優化執行效率。

重排序分為三種型別:

  1. 編譯器優化的重排序

    編譯器在不改變單執行緒程式語義的前提下,可以重新安排語句的執行順序。

  2. 指令集並行的重排序

    現代處理器採用指令集並行技術將多條指令重疊執行,如果不存在資料依賴,處理器可以改變語句對應機器指令的執行順序。

  3. 記憶體系統的重排序

    由於處理器使用快取和讀寫緩衝區,這使得載入和儲存操作看上去可能是在亂序執行。

從執行到重排序過程如下:

4033480-941ef62fbd6e448b.png

為了保證記憶體的可見性,Java編譯器在生成指令序列的適當位置會插入記憶體屏障來禁止特定型別的處理器重排序,記憶體屏障分為以下四種:

  • LoadLoad
  • LoadStore
  • StoreLoad
  • StoreStore
4033480-10bebdd0bb96eb5c.png

參考文章

殘雪餘香-Java記憶體模型

深入理解JVM——JVM記憶體模型

相關文章