1. 程式人生 > 程式設計 >Java記憶體模型

Java記憶體模型

Java記憶體模型

1. 前言

Java 記憶體模型是根據英文Java Memory Model(JMM)翻譯過來的。其實JMM並不像JVM記憶體結構一樣是真實存在的。他只是一個抽象的概念。用於遮蔽掉各種硬體和作業系統的記憶體訪問差異,以實現讓 Java 程式在各種平臺下都能達到一致的併發效果。

在 Java 面試中,一般會問 Java 記憶體模型,本篇將揭開 Java 記憶體模型神祕的面紗。

2. Java 記憶體模型的抽象結構

併發程式設計要解決的關鍵問題:

  1. 執行緒之間如何進行通訊
  2. 執行緒之間如何進行同步

通訊是指執行緒之間以何種機制進行交換資訊。在命令列程式設計中,執行緒之間的通訊機制有兩種:共享記憶體和訊息傳遞

。在共享記憶體模型中,執行緒之間共享程式的公共狀態,通過寫 - 讀記憶體中的公共狀態進行隱式通訊。在訊息傳遞的併發模型中,執行緒之間沒有公共狀態,執行緒之間必須通過傳送訊息顯式進行通訊。

同步是指程式中控制不同執行緒間操作發生相對順序的機制。在共享記憶體併發模型裡,同步時顯式進行的。

Java 的併發採用的是共享記憶體模型,Java 執行緒之間的通訊總是隱式進行,整個通訊過程是完全透明的。

Java 把記憶體分為兩個部分:

  • 主記憶體:儲存例項域、靜態域、陣列元素
  • 工作記憶體:儲存區域性變數、方法定義引數、異常處理器引數

JMM 抽象結構示意圖

Java 記憶體模型規定了所有的變數都儲存在主記憶體中,每條執行緒還有自己的工作記憶體,執行緒的工作記憶體中儲存了該執行緒中是用到的變數的主記憶體副本拷貝,執行緒對變數的所有操作都必須在工作記憶體中進行,而不能直接讀寫主記憶體。不同的執行緒之間也無法直接訪問對方工作記憶體中的變數,執行緒間變數的傳遞均需要自己的工作記憶體和主存之間進行資料同步進行。

而 JMM 就作用於工作記憶體和主存之間資料同步過程。他規定了如何做資料同步以及什麼時候做資料同步。

3. Java 記憶體模型的實現

在Java中提供了一系列和併發處理相關的關鍵字,比如 volatilesynchronizedfinalconcurren 包等。其實這些就是Java記憶體模型封裝了底層的實現後提供給程式設計師使用的一些關鍵字。

在開發多執行緒的程式碼的時候,我們可以直接使用 synchronized 等關鍵字來控制併發,從來就不需要關心底層的編譯器優化、快取一致性等問題。所以,Java 記憶體模型,除了定義了一套規範,還提供了一系列原語,封裝了底層實現後,供開發者直接使用。

4. JVM 記憶體結構

面試中,面試官問我Java記憶體模型,我說成了 JVM 記憶體結構,很多人都把這概念搞混了,其實完全不一樣,所以這裡也講講 JVM 記憶體結構。

《Java虛擬機器器規範(Java SE 8)》中描述了JVM執行時記憶體區域結構如下:

JVM 記憶體結構

  1. 以上是Java虛擬機器器規範,不同的虛擬機器器實現會各有不同,但是一般會遵守規範

  2. 規範中定義的方法區,只是一種概念上的區域,並說明瞭其應該具有什麼功能。但是並沒有規定這個區域到底應該處於何處。所以,對於不同的虛擬機器器實現來說,是由一定的自由度的。

  3. 不同版本的方法區所處位置不同,上圖中劃分的是邏輯區域,並不是絕對意義上的物理區域。因為某些版本的JDK中方法區其實是在堆中實現的

  4. 執行時常量池用於存放編譯期生成的各種字面量和符號應用。但是,Java語言並不要求常量只有在編譯期才能產生。比如在執行期,String.intern也會把新的常量放入池中。

  5. 除了以上介紹的JVM執行時記憶體外,還有一塊記憶體區域可供使用,那就是直接記憶體。Java虛擬機器器規範並沒有定義這塊記憶體區域,所以他並不由JVM管理,是利用本地方法庫直接在堆外申請的記憶體區域。

  6. 堆和棧的資料劃分也不是絕對的,如HotSpot的JIT會針對物件分配做相應的優化。

詳細的對比可以參考:JVM記憶體結構 VS Java記憶體模型 VS Java物件模型

5. 小結&參考資料

小結

JMM 是一種規範,目的是解決由於多執行緒通過共享記憶體進行通訊時,存在的本地記憶體資料不一致、編譯器會對程式碼指令重排序、處理器會對程式碼亂序執行等帶來的問題。對於 JMM 設計的關鍵字,後續文章會有部分講解。

參考資料