计算机内存体系与Java 内存模型
計算機內存相關硬件介紹與緩存一致性:
讓計算機并發執行若干個任務與更充分的利用計算機處理器的效能之間的因果關系,非常的復雜,其中一個重要的復雜性來源是絕大多數的運算任務都不可能只靠處理器計算就能完成的,處理器至少要與內存交互,如:讀取運算數據,存儲運算結果等。這個IO操作是很難消除的(無法僅靠寄存器來完成所有的運算任務)。由于計算機的存儲設備與處理器的運算速度有幾個數量級的差距,所以現代計算機系統都不得不加入一層讀寫速度盡可能接近處理器運算速度的高速緩存來作為內存與處理器之間的緩沖:將運算需要使用到的數據復制到緩沖中,讓運算能快速進行,當運算結束后再從緩存同步回主內存之中,這樣處理器就無需等待緩慢的內存讀寫了。
?
這里真是又不得不搬出大學時枯燥無聊的計算機系統結構了,真心后悔那會聽得云里霧里打瞌睡啊~~
覺得有必要先解釋一下處理器,寄存器,高速緩存,內存之間的關系:
處理器大家都非常了解,就是負責做邏輯運算的,也就是你寫的程序代碼最終都會變成一條條指令或計算公式,這些玩意呢到處理器里面就變成了二進制的各種組合,處理器計算后會得到一個結果。
?
寄存器是離處理器最近的一塊存儲介質,可以說位于內存模型的頂端,它的速度非常之快,快到可以和處理器相媲美,處理器從里面拿數據,運算完之后又把數據存回去。寄存器是處理器里面的一部分,處理器可能有多個寄存器,比如數據計數器,指令指針寄存器等等。
?
高速緩存是一個比內存速度快很多接近處理器速度的存儲區域,目的是把處理器要用到的一堆數據從主內存中復制進來供處理器享用,處理器享用完了之后又把結果同步回主內存,這樣處理器只做自己的事,而高速緩存就成了傳話筒。高速緩存有分為一級緩存,二級緩存和三級緩存,離處理器最近的是一級緩存,依次往后排,他們的容量大小也是由一往三 一級比一級大,所以你買電腦看CPU的時候可以關注下這些緩存,數值越大自然性能越好。
?
主內存是我們通常講的內存,比如現在的電腦動不動8G,16G啊等等,這些說的就是這個主內存,它比磁盤的讀寫速度快很多,但是又跟高速緩存沒法比,因此,程序啟動的時候,程序相關的數據會加載到主內存,然后處理器處理某塊邏輯的時候,比較占空間的東西會丟到主內存,比如Java里面的對象,就是存放在堆上面的,而Java虛擬機里面的堆就是放在主內存的。
?
那么問題來了,學挖掘機到底哪家強?(為啥處理器和內存之間要整的這么繁瑣?直接主內存就用高速緩存代替不是簡單粗暴高效嗎?)
答案很簡單:土豪請隨意!
?
高速緩存的造價是非常高昂的,而內存成本要低很多,但是成本低帶來的弊病也很明顯,速度慢!
其實饒了這么多,說白了就是費盡心思讓你花著白菜價享受這法拉利的速度體驗。
?
用一個比較好懂的例子幫助大家記憶他們之間的關系:
處理器好比皇帝,要處理每天的奏折,那么寄存器呢,就好比皇帝身邊的太監,負責給皇帝遞奏折,收奏折。皇帝身邊可能好幾個太監,比如專門遞緊急事務奏折的太監甲,遞一般性奏折的太監乙,遞上供美女奏折的太監丙等,寄存器也分很多種,每種都是為了配合CPU進行高速計算和儲存的。那么高速緩存呢,就好比朝中大臣,會收集各種重要信息,奏上去,最后這些奏折是通過太監遞給皇上的。主內存呢,你可以理解成各地地方官,大臣收集他們的訴求,并反饋給他們。
?
好了,啰嗦完了,我們現在回到開頭講的,基于高速緩存的存儲交互可以很好的解決了處理器與內存之間的矛盾,但是也為計算機系統帶來更高的復雜度,因為它引入了一個新的問題:緩存一致性。在多處理器系統中,每個處理器都擁有自己的高速緩存,而它們又共享同一個主內存,當多個處理器的運算任務都設計同一塊主內存區域時,將可能導致各自的緩存數據不一致問題。這也是我們為什么要做同步的原因。
?
Java內存模型
了解了計算機的一個大致內存模型也很容易了解Java的內存模型。
Java內存模型規定了所有的變量都存儲在主內存中(Java中的主內存和剛才上面講的計算機的主內存是一樣的,只不過Java是占用分配給虛擬機的那一部分),每個線程還有自己的工作內存(可與前面講的高速緩存類比),線程的工作內存中保存了被該線程使用到的變量的主內存副本拷貝(如果對象很大怎么辦呢,比如10個MB?虛擬機肯定不會那么傻把對象直接拷貝進去啦,會拷貝對象的地址,以及需要用到的字段的值,哪怕值也很大,比如大的字符串,也會有相應的機制保證不會全拷貝,不然不是直接就爆掉了么)。
線程對變量的所有操作(讀取,賦值等)都必須在工作內存中進行,而不能直接讀寫主內存中的變量。本人之前有寫過一篇博客《volatile的各種特性》,那么volatile可以保證任何情況下變量的可見性,是不是就是直接讀主內存呢,事實上,volatile變量依然有工作內存的拷貝,但是它的操作順序比較特殊,會每次都從主內存重新加載,所以你會看到每次volatile讀取到的都是最新的值。
不同的線程也無法訪問其他線程的工作內存中的變量,線程間變量值的傳遞需要通過主內存來完成。
這里說的Java的主內存,工作內存與Java堆,棧,方法區等并不是同一個層次的劃分,這兩者基本是沒有關系的,如果兩者一定要勉強對應起來,那從變量,主內存,工作內存的定義上看,主內存只要對應于Java堆中的對象實例數據部分,工作內存對應于虛擬機棧中的部分區域。從更低層次上來說,主內存就是直接對應于物理硬件的內存,而為了獲取更好的運行速度,虛擬機可能會讓工作內存優先存儲于寄存器和高速緩存區中,因為程序運行時主要訪問讀寫的是工作內存。
?
Java主內存與線程工作內存間8種交互操作
Java一個變量如何從主內存拷貝到工作內存呢,又如何從工作內存同步回主內存呢?Java內存模型定義了8中操作來完成,虛擬機保證下面的每一種操作都是原子的。
?
lock(鎖定):作用于主內存變量,它把一個變量標識為一條線程獨占的狀態。
unlock(解鎖):作用于主內存變量,它把一個處于鎖定狀態的變量釋放出來,釋放后的變量才能被其他線程鎖定。
read(讀取):作用于主內存變量,它把一個變量的值從主內存傳輸到線程的工作內存中,以便隨后的load動作使用。
load(載入):作用于工作內存的變量,它把read操作從主內存中得到的變量值放入工作內存的變量副本中。
use(使用):作用于工作內存的變量,把工作內存中一個變量的值傳遞給執行引擎,每當虛擬機遇到一個要使用變量的值的字節碼指令時,將會執行這個操作。
assign(賦值):作用于工作內存變量,它把一個從執行引擎接收到的值賦值給工作內存的變量,每當虛擬機遇到一個給變量賦值的字節碼指令時,執行這個操作。
store(存儲):作用于工作內存變量,它把工作內存中一個變量的值傳送到主內存中,以便隨后的write操作。
write(寫入):作用于主內存變量,它把store操作中從工作內存得到的變量的值放入主內存的變量中。
?
如果要把一個變量從主內存復制到工作內存,那就要順序的執行read和load操作,如果要把工作內存的變量同步會主內存,那就要順序的執行store和write操作。注意,Java內存模型只是要求上述兩個操作必須按順序執行,而沒有保證是連續執行。也就是說,read與load之間,store與write之間是可以插入其他指令的。此外,Java內存模型還規定了在執行上述8中操作時必須滿足如下規則:
1)不允許read和load,store和write操作之一單獨出現。即:不允許從主內存讀取了,但工作內存不要它,或者從工作內存寫出去的,主內存不要。
2)不允許一個線程丟棄它的最近的assign操作。即:變量在工作內存改變之后,必須同步回主內存。
3)不允許一個線程無原因地(沒有進行任何assign操作的)把數據從線程的工作內存同步回主內存中。
4)一個新的變量必須在主內存中誕生,不允許在工作內存中直接使用一個未被初始化(load和assign)的變量。
5)一個變量在同一時刻只允許一條線程對其進行lock操作,但lock操作可以被同一條線程重復執行多次。
6)如果對一個變量進行了lock操作,那將會清空工作內存中此變量的值,因此執行引擎使用這個變量之前,需要重新的進行load和assign操作。
7)如果一個變量事先沒有被lock操作鎖定,那就不允許對它執行unlock操作,也不允許去unlock一個其他線程鎖定住的變量。
8)對一個變量執行unlock操作之前,必須先把此變量同步回主內存之中。
?
參考:
《深入理解Java虛擬機》周志明著
總結
以上是生活随笔為你收集整理的计算机内存体系与Java 内存模型的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如果没有,那么就去创造
- 下一篇: 字符集的名词解释