1. 程式人生 > 實用技巧 >JVM記憶體模型1

JVM記憶體模型1

>>> hot3.png

1、併發程式設計的兩個關鍵問題:執行緒通訊和執行緒同步

執行緒通訊的方式:共享記憶體和訊息傳遞 記憶體共享模式是隱式進行通訊的,而訊息傳遞則是顯示進行通訊的

執行緒同步:用於控制執行緒間操作發生的相對順序的機制,共享記憶體模式中執行緒同步是顯示發生的,而訊息傳遞中執行緒同步則是隱式發生的。

JAVA採用的是共享記憶體的模型進行執行緒間通訊和同步的。

2、JAVA記憶體

在JAVA中,所有例項域、靜態域和陣列元素都儲存在堆記憶體中,而堆記憶體則是在多個執行緒中進行記憶體共享的,

區域性變數,方法定義引數,以及異常處理引數不會再執行緒之間共享,是執行緒獨有的,因此不會再多個執行緒中共享,也不會有執行緒間的可見性問題。

3、Java記憶體模型

Java執行緒之間的通訊由Java記憶體模型(本文簡稱為JMM)控制,JMM決定一個執行緒對共享變數的寫入何時對另一個執行緒可見。從抽象的角度來看,JMM定義了執行緒和主記憶體之間的抽象關係:執行緒之間的共享變數儲存在主記憶體(Main Memory)中,每個執行緒都有一個私有的本地記憶體(Local Memory),本地記憶體中儲存了該執行緒以讀/寫共享變數的副本。本地記憶體是JMM的一個抽象概念,並不真實存在。它涵蓋了快取、寫緩衝區、暫存器以及其他的硬體和編譯器優化。

4、重排序

1、編譯器優化的重排序:

編譯器在不改變單執行緒語義的情況下,可以重新安排語句的執行順序

2、指令級並行的重排序:

現代處理器採用了指令級並行技術來將多條指令重疊執行,如果不存在資料依賴性,處理器可以改變語句對應的機器指令的執行順序。——這裡應該指的就是流水線技術導致的指令並行的重排序

3、記憶體系統的重排序

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

上述的1屬於編譯器重排序,2和3屬於處理器重排序。這些重排序可能會導致多執行緒程式出現記憶體可見性問題。對於編譯器,JMM的編譯器重排序規則會禁止特定型別的編譯器重排序(不是所有的編譯器重排序都要禁止)。對於處理器重排序,JMM的處理器重排序規則會要求Java編譯器在生成指令序列時,插入特定型別的記憶體屏障(Memory Barriers,Intel稱之為Memory Fence)指令,通過記憶體屏障指令來禁止特定型別的處理器重排序。

4、處理器的重排序

常見的處理器允許store-load的重排序,不允許有資料依賴的重排序。處理器重排序產生問題是因為每個處理器都要一個寫緩衝區,而寫緩衝區對於其他處理器來說不是透明的,且寫緩衝區是批量處理的,因此可能重排序會產生問題。

StoreLoad Barriers是一個“全能型”的屏障,它同時具有其他3個屏障的效果。現代的多處理器大多支援該屏障(其他型別的屏障不一定被所有處理器支援)。執行該屏障開銷會很昂貴,因為當前處理器通常要把寫緩衝區中的資料全部重新整理到記憶體中(Buffer Fully Flush)。

5、as-if-serial語義

as-if-serial語義的意思是:不管怎麼重排序(編譯器和處理器為了提高並行度),(單執行緒)程式的執行結果不能被改變。編譯器、runtime和處理器都必須遵守as-if-serial語義

6、未同步的程式在JMM中的執行順序

A、順序一致性模型保證了單執行緒內的操作會按照程式的順序執行,而JMM不保證單執行緒內的操作會按照程式的順序執行(比如多執行緒程式在臨界區內的重排序)

B、順序一致性模型保證所有執行緒只能看到一致的操作順序,而JMM不保證所有執行緒能看到一致的操作執行順序

C、JMM不保證對64位的long型和double型的變數寫操作具有原子性,而順序一致性模型保證對所有的記憶體讀/寫操作都具有原子性(因為64位的變數一般會分解成2次32位變數的讀寫,因此不具有一致性,但是現在JMM要求讀變數必須具有一致性,也就是說64位的讀也是原子的,但是寫變數建議具有一致性,這是為了效能考量)

轉載於:https://my.oschina.net/guanhe/blog/2222702