前言
前一陣在看volatile的原理,看到內存屏障和緩存一致性,發現再往底層挖就挖到了硬件和Java內存模型。這一塊是自己似懂非懂的知識區,我一般稱之為知識混沌區。因此整理這一篇文章。
什么是內存模型(Memory?Model)呢?它是系統和程序員之間的規范,它規定了存儲器訪問的行為,并影響到了性能。并且,Memory?Model有多層,處理器規定、編譯器規定、高級語。對于高級語言來說,如Java內存模型,?它通常需要支持跨平臺,也就是說它會基于各種不同的內存模型,但是又要提供給程序員一個統一的內存模型,可以理解為一個適配器的角色。
這篇文字主要包含了三部分:
-
硬件內存模型
-
Java內存模型和運行時數據區
-
三者的關系
硬件內存模型
下圖是2012?Sandy?Bridge(一種Intel處理器)核心設計。圖中有socket1和socket2,可以理解為了這臺電腦中有兩個插cpu的槽,每個槽中有一個多核(C1,C2...Cn)的cpu。對于CPU的內存模型可以大致按照如下進行分解:
寄存器 | 編譯器會將本地變量和函數參數分配到這些寄存器上 |
內存排序緩沖(Memory?Ordering?Buffers) | 這些緩沖用于記錄等待緩存子系統時正在執行的操作 |
L1?緩存 | 空間最小,訪問速度最快 |
L2緩存 | 要作用是作為L1和L3之間的高效內存訪問隊列。L2緩存同時包含數據和指令 |
L3緩存 | 在同插槽的所有核心都共享L3緩存。 |
主內存 | 在緩存完全沒命中的情況下 |
簡化之后如下圖所示,計算機在高速的?CPU?和相對低速的存儲設備(內存,RAM)之間使用高速緩存,作為內存和處理器之間的緩沖。將運算需要使用到的數據復制到緩存中,讓運算能快速運行,當運算結束后再從緩存同步回內存之中。
在多處理器的系統中(或者單處理器多核的系統),每個處理器內核都有自己的高速緩存,它們有共享同一主內存(Main?Memory---RAM)。
Java內存模型和運行時數據區
說完系統的內存模型,然后開始說一下Java的內存模型(Java?Memory?Model,簡稱?JMM)。
????
Java語言一大特性就是跨平臺型。其背后的實現便是在Java?虛擬機規范中定義的Java?內存模型(Java?Memory?Model,簡稱?JMM)。
虛擬機通過內存模型,定義了將變量存儲到內存和從內存中取出變量的底層細節(字節碼指令轉換是另一方面),屏蔽掉各種硬件和操作系統的內存訪問差異,實現讓?Java?程序在各種平臺下都能達到一致的內存訪問效果,不必因為不同平臺上的物理機的內存模型的差異,對各平臺定制化開發程序。
上面提到了兩個重要的概念--內存和變量。
先說內存,Java內存模型中的內存分為兩類:主內存(Main?Memory)和工作內存(Working?Memory,又稱本地內存)。其詳情如下:
主內存可以類比成物理硬件的主內存,但此處僅是虛擬機內存的部分。工作內存可以類比成處理器高速緩存
主內存是所有的線程共享,每個線程都有自己的工作內存,屬于線程私有。
一個線程不能訪問另一個線程的工作內存,線程之間需要通過主內存來實現線程間的通信;
線程的工作內存中保存了該線程使用到的變量的主內存副本拷貝,線程對變量的所有的操作(讀取、賦值等)都必須在工作內存中進行,而不能直接讀寫主內存的變量;
如果想理解上面提到的變量(Variables)的提取和存儲。那就需要繼續看一下JVM另一個重要的知識點--運行時數據區。如下圖所示,不同jdk版本的運行時數據區劃分有所差異。但主要分為了五部分:方法區(8開始使用元數據),堆,棧,程序計數器,本地方法棧。細節就不展開講。
內存模型中管理的變量(Variables)則主要指是堆中的數據,它包括了實例字段、靜態字段和構成數值對象的元素,如數組等。但不包括Java方法中局部變量與方法參數,如果局部變量是一個?reference?類型,它引用的對象在?Java?堆中可被各個線程共享,但是?reference?本身在?Java?棧的局部變量表中。
總結一下:Java?運行時數據區和內存模型是不一樣的東西,更確切的說應該是不是同一層次的東西。
-
內存模型:是定義了線程和主內存之間的抽象關系,更多的是定義規則
-
運行時數據區域:是指?JVM?運行時將數據分區域存儲,強調對內存空間的劃分。
硬件內存模型和Java內存模型關系
Java內存模型中的主內存和工作內存的區別在于線程調度和共享的區別,而不是物理層面的區別。因此硬是將Java內存模型和硬件內存模型進行映射沒有意義。如果真的有關系,只能說工作內存更多的是CPU寄存器和緩存。當然,工作內存由于上下文切換等原因,也可能會寫回RAM(可以理解為物理內存,嚴謹來說是物理虛擬內存)。
對于運行時數據區而言,有些是隨著虛擬機啟動而創建,虛擬機關閉而銷毀。還有一部分是隨著線程生命周期創建銷毀的。
線程間共享的方法區和堆,是說它們會隨著虛擬機啟動而創建,隨著虛擬機退出而銷毀。而棧和程序計數器會隨著線程開始和結束而創建和銷毀。
正如下圖所示,方法區和堆在RAM中,也可以在緩存中,這也是JVM創建時進行管理的內存空間。
以堆為例,由于對象實例的創建在JVM中非常頻繁,一方面保證并發環境下從堆區中劃分內存空間的線程安全,另一方面提升內存分配的吞吐量。對Eden區域繼續進行劃分,JVM為每個線程分配一個私有緩存區域--本地線程分配緩沖((Thread?Local?Allocation?Buffer,TLAB)。而這個緩沖則緩存中,而不是RAM中
如前所述,Java內存模型和硬件內存架構是不同的。?線程棧和堆的一部分有時可能存在于CPU高速緩存和內部CPU寄存器中。?
參考資料:
https://jenkov.com/tutorials/java-concurrency/java-memory-model.html
指令重排序 - 簡書
https://www.cnblogs.com/czwbig/p/11127124.html
https://zhuanlan.zhihu.com/p/51613784
Java 運行時數據區和內存模型(JMM)_jmm 和運行時數據區 的關系看-CSDN博客
簡單介紹一下什么是“工作內存”和“主內存”(JMM中的概念)_工作內存和主內存-CSDN博客
JVM運行時數據區------堆_數據區和堆-CSDN博客