Java內存模型

主內存與工做內存

Java內存模型的主要目標是定義程序中各個變量的訪問規則,即在虛擬機中將變量存儲到內存和從內存中取出變量這樣的底層細節。此處的變量(Variable)與Java編程中所說的變量略有區別,它包括了實例字段、靜態字段和構成數組對象的元素,可是不包括局部變量與方法參數,由於後者是線程私有的,不會被共享,天然就不存在競爭問題。爲了得到較好的執行效能,Java內存模型並無限制執行引擎使用處理器的特定寄存器或緩存來和主內存進行交互,也沒有限制即時編譯器調整代碼執行順序這類權利。java

Java內存模型規定了全部的變量都存儲在主內存(MainMemory)中(此處的主內存與介紹物理硬件時的主內存名字同樣,二者也能夠互相類比,但此處僅是虛擬機內存的一部分)。每條線程還有本身的工做內存(WorkingMemory,可與前面所講的處理器高速緩存類比),線程的工做內存中保存了被該線程使用到的變量的主內存副本拷貝,線程對變量的全部操做(讀取、賦值等)都必須在工做內存中進行,而不能直接讀寫主內存中的變量。不一樣的線程之間也沒法直接訪問對方工做內存中的變量,線程間變量值的傳遞均須要經過主內存來完成,線程、主內存、工做內存三者的交互關係以下圖所示。編程

圖 1  線程、主內存、工做內存三者的交互關係數組

這裏所講的主內存、工做內存與本書第2章所講的Java內存區域中的Java堆、棧、方法區等並非同一個層次的內存劃分。若是二者必定要勉強對應起來,那從變量、主內存、工做內存的定義來看,主內存主要對應於Java堆中對象的實例數據部分[5],而工做內存則對應於虛擬機棧中的部分區域。從更低的層次來講,主內存就是硬件的內存,而爲了獲取更好的運行速度,虛擬機及硬件系統可能會讓工做內存優先存儲於寄存器和高速緩存中。緩存

內存間交互操做

關於主內存與工做內存之間具體的交互協議,即一個變量如何從主內存拷貝到工做內存、如何從工做內存同步回主內存之類的實現細節,Java內存模型中定義瞭如下八種操做來完成:線程

  • lock(鎖定):做用於主內存的變量,它把一個變量標識爲一條線程獨佔的狀態。
  • unlock(解鎖):做用於主內存的變量,它把一個處於鎖定狀態的變量釋放出來,釋放後的變量才能夠被其餘線程鎖定。
  • read(讀取):做用於主內存的變量,它把一個變量的值從主內存傳輸到線程的工做內存中,以便隨後的load動做使用。
  • load(載入):做用於工做內存的變量,它把read操做從主內存中獲得的變量值放入工做內存的變量副本中。
  • use(使用):做用於工做內存的變量,它把工做內存中一個變量的值傳遞給執行引擎,每當虛擬機遇到一個須要使用到變量的值的字節碼指令時將會執行這個操做。
  • assign(賦值):做用於工做內存的變量,它把一個從執行引擎接收到的值賦值給工做內存的變量,每當虛擬機遇到一個給變量賦值的字節碼指令時執行這個操做。
  • store(存儲):做用於工做內存的變量,它把工做內存中一個變量的值傳送到主內存中,以便隨後的write操做使用。
  • write(寫入):做用於主內存的變量,它把store操做從工做內存中獲得的變量的值放入主內存的變量中。

若是要把一個變量從主內存複製到工做內存,那就要按順序地執行read和load操做,若是要把變量從工做內存同步回主內存,就要按順序地執行store和write操做。注意,Java內存模型只要求上述兩個操做必須按順序執行,而沒有保證必須是連續執行。也就是說read與load之間、store與write之間是可插入其餘指令的,如對主內存中的變量a、b進行訪問時,一種可能出現的順序是reada、readb、loadb、loada。除此以外,Java內存模型還規定了在執行上述八種基本操做時必須知足以下規則:對象

  • 不容許read和load、store和write操做之一單獨出現,即不容許一個變量從主內存讀取了但工做內存不接受,或者從工做內存發起回寫了但主內存不接受的狀況出現。
  • 不容許一個線程丟棄它的最近的assign操做,即變量在工做內存中改變了以後必須把該變化同步回主內存。
  • 不容許一個線程無緣由地(沒有發生過任何assign操做)把數據從線程的工做內存同步回主內存中。
  • 一個新的變量只能在主內存中「誕生」,不容許在工做內存中直接使用一個未被初始化(load或assign)的變量,換句話說就是對一個變量實施use和store操做以前,必須先執行過了assign和load操做。
  • 一個變量在同一個時刻只容許一條線程對其進行lock操做,但lock操做能夠被同一條線程重複執行屢次,屢次執行lock後,只有執行相同次數的unlock操做,變量纔會被解鎖。
  • 若是對一個變量執行lock操做,將會清空工做內存中此變量的值,在執行引擎使用這個變量前,須要從新執行load或assign操做初始化變量的值。
  • 若是一個變量事先沒有被lock操做鎖定,則不容許對它執行unlock操做;也不容許去unlock一個被其餘線程鎖定住的變量。
  • 對一個變量執行unlock操做以前,必須先把此變量同步回主內存中(執行sotre和write操做)。

 參考資料

周志明:<深刻理解Java虛擬機>blog

深刻理解Java內存模型(一)——基礎:http://www.infoq.com/cn/articles/java-memory-model-1內存

相關文章
相關標籤/搜索