java內存模型

cpu緩存與內存

計算機工做時,因爲cpu的計算任務速度遠遠大於從內存讀取數據的速度,因此cpu引入了緩存,存取要使用的資源。java

1 爲何cpu緩存能夠解決速度不匹配問題算法

緩存的存在依據如下原理:
1 時間局部性:若是某個數據被訪問,那麼在不久的未來,它將再次被訪問 2 空間局部性:若是某個數據被訪問,那麼與它相鄰的數據塊也將被訪問 這樣當cpu 在計算時計算資源可能已經存儲在緩存中,cpu就不須要到內存中去加載,cpu能夠直接進行計算。

若是cpu是單核,那麼整個cpu只有一套L三、L二、L1緩存(如今計算機大部分都是3級緩存);若是cpu是多核,則每一個核心都含有一套L1(甚至和L2)緩存,而共享L3(或者和L2)緩存。因此會出現cpu緩存中數據不一致的問題。基於此CPU引入了緩存一致性模型(MESI)。 image緩存

M : modify 這行數據有效,數據被修改了,和內存中的數據不一致,數據只存在於本Cache中,因此處在該狀態下的數據要時間監聽是否有其餘緩存在內存中讀取該行,若是有必須延遲到本地cache將這行數據刷新到主存中,而且將狀態改成S(共享)才能執行。 E : Exclusive 這行數據有效,數據和內存中的數據一致,數據只存在於本Cache中,緩存行也必須監聽其餘緩存讀取內存中該緩存行的數據,若是有則該緩存行須要修改狀態爲S S : Shared 這行數據有效,數據和內存中的數據一致,數據存在於cache中。緩存行業必須監聽其餘緩存使該緩存行無效或者獨享該緩存行的請求。 I : 這行數據無效

java內存

java虛擬機爲了屏蔽各類硬件和操做系統之間的差別,引入了java內存模型(簡稱JMM),用以實現java程序在各類平臺下都能一致性的訪問。. java內存模型是一種規範,它規範了java虛擬機和計算機內存是如何協同工做的,規定了一個線程如何和什麼時候能看到其餘線程修改過的共享變量,以及在必須時如何同步的訪問共享變量。 image多線程

Java的內存主要分爲程序計數器、堆、棧、方法區。spa

  • 程序計數器

程序計數器是一塊較小的內存空間,能夠看做是當前線程所執行的字節碼的指示器、屬於線程私有的。java虛擬機的多線程是經過線程輪流切換並分配處理器執行時間的方式實現的。因此程序計數器用來標識此線程執行到哪一個指令。操作系統

棧也是線程私有的,它的生命週期與線程相同。虛擬機棧描述的是java方法執行的內存模型,java方法在執行時都會建立一個棧幀用於存儲局部變量表、操做數棧、動態連接、方法出口等信息。主要存放局部變量、基本類型、對應的引用。線程

出錯是拋出兩種異常:code

1 若是線程請求的棧的深度大於虛擬機所容許的深度,將拋出StackoverflowEorror的異常blog

2 若是虛擬機棧能夠能夠動態擴展,擴展時沒有合適的空間則拋出oom異常。生命週期

  • 堆是java虛擬機管理的最大一塊內存。java的堆是全部線程共享的,此內存區域的惟一的惟一目的就是存放實例。java經過垃圾收集器來對此塊內存進行管理。如今的收集器基本都採用分代收集算法,因此java堆能夠細分爲新生代和老年代,其中新生代又分爲Eden空間,From Survivor空間、To Survivor空間。

  • 方法區

    java方法區與java堆同樣,是各個線程共享的內存區域,它用於存儲已被虛擬機加載的類信息、常量、靜態變量即時編譯器編譯後的代碼等數據。

因此每一個線程在工做時,都會將存儲在主存中的數據拷貝到工做內存中,此工做內存指的是cpu的緩存(或者寄存器)。這樣當數據被加載到每一個線程的緩存的時候,當多個線程同時讀取了某一共享變量的時候,就會形成數據不一致的問題。java虛擬機爲了解決該問題引入了java內存模型。

Java虛擬機定義了8種操做和幾個規則來實現內存模型。其中8種操做包括 lock(鎖定)、read(讀取)、load(載入)、use(使用)、asign(賦值)、store(存儲)、write(寫入)、解鎖。以後對這八種操做定義了使用它們的規則來保證數據的完整、一致。 image

  • lock 做用於主內存將一個變量標記爲一個線程獨自佔有
  • read 做用於主內存,將變量值從主內存中讀到工做內存中
  • load 做用於工做內存,將變量值賦值到工做內存中對應的變量上
  • use 做用於工做內存,把工做內存中的一個變量值傳遞給執行引擎
  • assign 做用於工做內存的變量,它把一個從執行引擎接收到的值賦給工做內存的變量
  • store 做用於工做內存的變量,把工做內存中的一個變量的值傳送到主內存中,以便隨後的write的操做
  • write 做用於工做內存的變量,它把store操做從工做內存中的一個變量的值傳送到主內存的變量中

若是要把一個變量從主內存中複製到工做內存中,就須要按順序地執行read和load操做,若是把變量從工做內存中同步到主內存中,就須要按順序地執行store和write操做。但Java內存模型只要求上述操做必須按順序執行,而沒有保證必須是連續執行。

同步規則

  1. 不容許一個線程無緣由地(沒有發生過任何assign操做)把數據從工做內存同步會主內存中
  2. 一個新的變量只能在主內存中誕生,不容許在工做內存中直接使用一個未被初始化(load或者assign)的變量。即就是對一個變量實施use和store操做以前,必須先自行assign和load操做。
  3. 一個變量在同一時刻只容許一條線程對其進行lock操做,但lock操做能夠被同一線程重複執行屢次,屢次執行lock後,只有執行相同次數的unlock操做,變量纔會被解鎖。lock和unlock必須成對出現。
  4. 若是對一個變量執行lock操做,將會清空工做內存中此變量的值,在執行引擎使用這個變量以前須要從新執行load或assign操做初始化變量的值。
  5. 若是一個變量事先沒有被lock操做鎖定,則不容許對它執行unlock操做;也不容許去unlock一個被其餘線程鎖定的變量。
  6. 對一個變量執行unlock操做以前,必須先把此變量同步到主內存中(執行store和write操做
相關文章
相關標籤/搜索