1.CPU多級(jí)緩存
左圖為最簡(jiǎn)單的高速緩存的配置锌唾,數(shù)據(jù)的讀取和存儲(chǔ)都經(jīng)過高速緩存搏色,CPU核心與高速緩存有一條特殊的快速通道仰猖;主存與高速緩存都連在系統(tǒng)總線上(BUS)這條總線還用于其他組件的通信
在高速緩存出現(xiàn)后不久,系統(tǒng)變得越來越復(fù)雜神得,高速緩存與主存之間的速度差異被拉大厘惦,直到加入了另一級(jí)緩存,新加入的這級(jí)緩存比第一緩存更大循头,并且更慢绵估,而且經(jīng)濟(jì)上不合適,所以有了二級(jí)緩存卡骂,甚至是三級(jí)緩存
CPU多級(jí)緩存
為什么需要CPU cache国裳?:
CPU的頻率太快了,快到主存跟不上全跨,這樣在處理器時(shí)鐘周期內(nèi)缝左,CPU常常需要等待主存渺杉,浪費(fèi)資源是越,
所以cache的出現(xiàn)倚评,是為了緩解CPU和內(nèi)存之間速度的不匹配問題(結(jié)構(gòu):cpu->cache->memort)
CPU cache有什么意義:
1.時(shí)間局部性:如果某個(gè)數(shù)據(jù)被訪問,那么在不久的將來他很可能被再次訪問
2.空間局部性:如果某個(gè)數(shù)據(jù)被訪問呢岗,那么與他相鄰的數(shù)據(jù)很快也可能被訪問
緩存一致性(MESI)
? M: Modified 修改后豫,指的是該緩存行只被緩存在該CPU的緩存中挫酿,并且是被修改過的饭豹,因此他與主存中的數(shù)據(jù)是不一致的,
? ? ? 該緩存行中的數(shù)據(jù)需要在未來的某個(gè)時(shí)間點(diǎn)(允許其他CPU讀取主存相應(yīng)中的內(nèi)容之前)寫回主存翘悉,然后狀態(tài)變成E(獨(dú)享)
? E:Exclusive 獨(dú)享 緩存行只被緩存在該CPU的緩存中妖混,是未被修改過的,與主存的數(shù)據(jù)是一致的祥楣,可以在任何時(shí)刻當(dāng)有其他CPU讀取該內(nèi)存時(shí)误褪,變成S(共享)狀態(tài),當(dāng)CPU修改該緩存行的內(nèi)容時(shí)嘀略,變成M(被修改)的狀態(tài)
? S:Share 共享,意味著該緩存行可能會(huì)被多個(gè)CPU進(jìn)行緩存逮壁,并且該緩存中的數(shù)據(jù)與主存數(shù)據(jù)是一致的卖宠,當(dāng)有一個(gè)CPU修改該緩存行時(shí)扛伍,其他CPU是可以被作廢的刺洒,變成I(無效的)
? I:Invalid 無效的逆航,代表這個(gè)緩存是無效的,可能是有其他CPU修改了該緩存行
1.用于保證多個(gè)CPU cache之間緩存共享數(shù)據(jù)的一致
MESI
local read:讀本地緩存的數(shù)據(jù)
local write:將數(shù)據(jù)寫到本地緩存里面
remote read:將內(nèi)(主)存中的數(shù)據(jù)讀取到緩存中來
remote write:將緩存中的數(shù)據(jù)寫會(huì)到主存里面
在一個(gè)典型的多核系統(tǒng)中,每一個(gè)核都會(huì)有自己的緩存來共享主存總線澳眷,每一個(gè)CPU會(huì)發(fā)出讀寫(I/O)請(qǐng)求境蔼,而緩存的目的是為了減少CPU讀寫共享主存的次數(shù);
一個(gè)緩存除了在Invaild狀態(tài)吴藻,都可以滿足CPU 的讀請(qǐng)求
一個(gè)寫請(qǐng)求只有在M狀態(tài),或者E狀態(tài)的時(shí)候才能給被執(zhí)行航罗,如果是處在S狀態(tài)的時(shí)候粥血,他必須先將該緩存行變成I狀態(tài)趾娃,
這個(gè)操作通常作用于廣播的方式來完成,這個(gè)時(shí)候他既不允許不同的CPU同時(shí)修改同一個(gè)緩存行笤成,即使是修改同一個(gè)緩存行中不同端的數(shù)據(jù)也是不可以的,這里主要解決的是緩存一致性的問題,
一個(gè)M狀態(tài)的緩存行必須時(shí)刻監(jiān)聽所有試圖讀該緩存行相對(duì)主存的操作,這種操作必須在緩存該緩存行被寫會(huì)到主存,并將狀態(tài)變成S狀態(tài)之前掉蔬,被延遲執(zhí)行
一個(gè)處于S狀態(tài)的緩存行,也必須監(jiān)聽其他緩存使該緩存行無效蛉迹,或者獨(dú)享該緩存行的請(qǐng)求,并將緩存行變成無效
一個(gè)處于E狀態(tài)的緩存行,他要監(jiān)聽其他緩存讀緩存行的操作攘宙,一旦有肩民,那么他講變成S狀態(tài)
因此對(duì)于M和E狀態(tài),他們的數(shù)據(jù)總是一致的與緩存行的真正狀態(tài)總是保持一致的工窍,
但是S狀態(tài)可能是非一致的罢维,如果一個(gè)緩存將處于S狀態(tài)的 緩存行作廢了匀借,另一個(gè)緩存可能已經(jīng)獨(dú)享了該緩存行,
但是該緩存缺不會(huì)講該緩存行升遷為E狀態(tài),這是因?yàn)槠渌彺娌粫?huì)廣播他們已經(jīng)作廢掉該緩存行的通知,
同樣由于緩存并沒有保存該緩存行被COPY的數(shù)量,因此沒有辦法確定是否獨(dú)享了改緩存行紫皇,
這是一種投機(jī)性的優(yōu)化萄窜,因?yàn)槿绻粋€(gè)CPU想修改一個(gè)處于S狀態(tài)的緩存行键兜,總線需要將所有使用該緩存行的COPY的值變成Invaild狀態(tài)才可以,而修改E狀態(tài)的緩存缺不需要使用總顯示無
2.CPU多級(jí)緩存的亂序執(zhí)行優(yōu)化
處理器為提高運(yùn)算速度而做出違背代碼原有順序的優(yōu)化
CPU亂序
導(dǎo)致的一個(gè)問題仔沿,如果我們不做任何處理,在多核的情況下,的實(shí)際結(jié)果可能和邏輯運(yùn)行結(jié)果大不相同脊阴,如果在一個(gè)核上執(zhí)行數(shù)據(jù)寫入操作,并在最后執(zhí)行一個(gè)操作來標(biāo)記數(shù)據(jù)已經(jīng)寫入好了蜜猾,而在另外一個(gè)核上通過該標(biāo)記位判定數(shù)據(jù)是否已經(jīng)寫入,這時(shí)候就可能出現(xiàn)不一致脊串,標(biāo)記位先被寫入怖侦,但是實(shí)際的操作缺并未完成念赶,這個(gè)未完成既有可能是沒有計(jì)算完成础钠,也有可能是緩存沒有被及時(shí)刷新到主存之中,使得其他核讀到了錯(cuò)誤的數(shù)據(jù)
3.Java內(nèi)存模型(Java Memory Model叉谜,JMM)
JAVA內(nèi)存模型規(guī)范:
1.規(guī)定了一個(gè)線程如何和何時(shí)可以看到其他線程修改過后的共享變量的值
2.如何以及何時(shí)同步的訪問共享變量
JAVA內(nèi)存模型:
java內(nèi)存分配
Heap(堆):java里的堆是一個(gè)運(yùn)行時(shí)的數(shù)據(jù)區(qū)旗吁,堆是由垃圾回收來負(fù)責(zé)的,? ? ? ? 堆的優(yōu)勢(shì)是可以動(dòng)態(tài)的分配內(nèi)存大小停局,生存期也不必事先告訴編譯器很钓,? ? ? ? 因?yàn)樗窃谶\(yùn)行時(shí)動(dòng)態(tài)分配內(nèi)存的,java的垃圾回收器會(huì)定時(shí)收走不用的數(shù)據(jù)董栽,? ? ? ? 缺點(diǎn)是由于要在運(yùn)行時(shí)動(dòng)態(tài)分配码倦,所有存取速度可能會(huì)慢一些Stack(棧):棧的優(yōu)勢(shì)是存取速度比堆要快,僅次于計(jì)算機(jī)里的寄存器锭碳,棧的數(shù)據(jù)是可以共享的袁稽,? ? ? ? ? 缺點(diǎn)是存在棧中的數(shù)據(jù)的大小與生存期必須是確定的,缺乏一些靈活性? ? ? ? ? 棧中主要存放一些基本類型的變量擒抛,比如int推汽,short,long歧沪,byte歹撒,double,float诊胞,boolean暖夭,char,對(duì)象句柄撵孤,java內(nèi)存模型要求調(diào)用棧和本地內(nèi)存變量存放在線程棧(Thread Stack)上迈着,對(duì)象存放在堆上。一個(gè)本地變量可能存放一個(gè)對(duì)象的引用邪码,這時(shí)引用變量存放在本地棧上寥假,但是對(duì)象本身存放在堆上成員變量跟隨著對(duì)象存放在堆上,而不管是原始類型還是引用類型霞扬,靜態(tài)成員變量跟隨著類的定義一起存在在堆上存在堆上的對(duì)象糕韧,可以被持有這個(gè)對(duì)象的引用的線程訪問如果兩個(gè)線程同時(shí)訪問同一個(gè)對(duì)象的私有變量枫振,這時(shí)他們獲得的是這個(gè)對(duì)象的私有拷貝
計(jì)算機(jī)硬件架構(gòu):
計(jì)算機(jī)架構(gòu)圖示
CPU:一個(gè)計(jì)算機(jī)一般有多個(gè)CPU,一個(gè)CPU還會(huì)有多核
CPU Registers(寄存器):每個(gè)CPU都包含一系列的寄存器萤彩,他們是CPU內(nèi)存的基礎(chǔ)粪滤,CPU在寄存器上執(zhí)行的速度遠(yuǎn)大于在主存上執(zhí)行的速度。
CPU Cache(高速緩存):由于計(jì)算機(jī)的存儲(chǔ)設(shè)備與處理器的處理設(shè)備有著幾個(gè)數(shù)量級(jí)的差距雀扶,
? ? ? ? ? ? ? ? ? ? 所以現(xiàn)代計(jì)算機(jī)都會(huì)加入一層讀寫速度與處理器處理速度接近想通的高級(jí)緩存來作為內(nèi)存與處理器之間的緩沖杖小,
? ? ? ? ? ? ? ? ? ? 將運(yùn)算使用到的數(shù)據(jù)復(fù)制到緩存中,讓運(yùn)算能夠快速的執(zhí)行愚墓,當(dāng)運(yùn)算結(jié)束后予权,再?gòu)木彺嫱降絻?nèi)存之中,這樣浪册,CPU就不需要等待緩慢的內(nèi)存讀寫了
主(內(nèi))存:一個(gè)計(jì)算機(jī)包含一個(gè)主存扫腺,所有的CPU都可以訪問主存,主存比緩存容量大的多
運(yùn)作原理:通常情況下村象,當(dāng)一個(gè)CPU要讀取主存的時(shí)候笆环,他會(huì)將主存中的數(shù)據(jù)讀取到CPU緩存中,甚至將緩存中的內(nèi)容讀到內(nèi)部寄存器里面厚者,然后再寄存器執(zhí)行操作躁劣,
當(dāng)運(yùn)行結(jié)束后,會(huì)將寄存器中的值刷新回緩存中库菲,并在某個(gè)時(shí)間點(diǎn)刷新回主存
內(nèi)存模型與硬件架構(gòu)之間的關(guān)聯(lián):
內(nèi)存模型與硬件架構(gòu)之間的關(guān)聯(lián)
所有線程棧和堆會(huì)被保存在緩存里面账忘,部分可能會(huì)出現(xiàn)在CPU緩存中和CPU內(nèi)部的寄存器里面
線程和主內(nèi)存的抽象關(guān)系
線程和主內(nèi)存的抽象關(guān)系
每個(gè)線程之間共享變量都存放在主內(nèi)存里面,每個(gè)線程都有一個(gè)私有的本地內(nèi)存
本地內(nèi)存是java內(nèi)存模型中抽象的概念熙宇,并不是真實(shí)存在的(他涵蓋了緩存寫緩沖區(qū)鳖擒。寄存器,以及其他硬件的優(yōu)化)
本地內(nèi)存中存儲(chǔ)了以讀或者寫共享變量的拷貝的一個(gè)副本
從一個(gè)更低的層次來說奇颠,線程本地內(nèi)存败去,他是cpu緩存放航,寄存器的一個(gè)抽象描述烈拒,而JVM的靜態(tài)內(nèi)存存儲(chǔ)模型,
他只是一種對(duì)內(nèi)存模型的物理劃分而已广鳍,只局限在內(nèi)存荆几,而且只局限在JVM的內(nèi)存
如果線程A和線程B要通信,必須經(jīng)歷兩個(gè)過程:
1赊时、A將本地內(nèi)存變量刷新到主內(nèi)存
2吨铸、B從主內(nèi)存中讀取變量
八種同步操作
1.lock(鎖定):作用于主內(nèi)存的變量,把一個(gè)變量標(biāo)識(shí)變?yōu)橐粭l線程獨(dú)占狀態(tài)
2.unlock(解鎖):作用于主內(nèi)存的變量祖秒,把一個(gè)處于鎖定狀態(tài)的變量釋放出來诞吱,釋放后的變量才可以被其他線程鎖定
3.read(讀戎鄣臁):作用于主內(nèi)存的變量,把一個(gè)變量值從主內(nèi)存?zhèn)鬏數(shù)骄€程的工作內(nèi)存中房维,以便隨后的load動(dòng)作使用
4.load(載入):作用于工作內(nèi)存的變量沼瘫,它把read操作從主內(nèi)存中得到的變量值放入工作內(nèi)存的變量副本中
5.use(使用):作用于工作內(nèi)存的變量,把工作內(nèi)存中的一個(gè)變量值傳遞給執(zhí)行引擎
6.assign(賦值):作用于工作內(nèi)存的變量咙俩,它把一個(gè)從執(zhí)行引擎接受到的值賦值給工作內(nèi)存的變量
7.store(存儲(chǔ)):作用于工作內(nèi)存的變量耿戚,把工作內(nèi)存中的一個(gè)變量的值傳送到主內(nèi)存中,以便隨后的write的操作
8.write(寫入):作用于主內(nèi)存的變量阿趁,它把store操作從工作內(nèi)存中一個(gè)變量的值傳送到主內(nèi)存的變量中
同步規(guī)則
1.如果要把一個(gè)變量從主內(nèi)存中賦值到工作內(nèi)存膜蛔,就需要按順序得執(zhí)行read和load操作,如果把變量從工作內(nèi)存中同步回主內(nèi)存中脖阵,就要按順序得執(zhí)行store和write操作皂股,但java內(nèi)存模型只要求上述操作必須按順序執(zhí)行,沒有保證必須是連續(xù)執(zhí)行
2.不允許read和load独撇、store和write操作之一單獨(dú)出現(xiàn)
3.不允許一個(gè)線程丟棄他的最近assign的操作屑墨,即變量在工作內(nèi)存中改變了之后必須同步到主內(nèi)存中
4.不允許一個(gè)線程無原因地(沒有發(fā)生過任何assign操作)把數(shù)據(jù)從工作內(nèi)存同步到主內(nèi)存中
5.一個(gè)新的變量只能在主內(nèi)存中誕生,不允許在工作內(nèi)存中直接使用一個(gè)未被初始化(load或assign)的變量纷铣。即就是對(duì)一個(gè)變量實(shí)施use和store操作之前卵史,必須先執(zhí)行過了load和assign操作
6.一個(gè)變量在同一時(shí)刻只允許一條線程對(duì)其進(jìn)行l(wèi)ock操作,但lock操作可以同時(shí)被一條線程重復(fù)執(zhí)行多次搜立,多次執(zhí)行l(wèi)ock后以躯,只有執(zhí)行相同次數(shù)的unlock操作,變量才會(huì)解鎖啄踊,lock和unlock必須成對(duì)出現(xiàn)
7.如果一個(gè)變量執(zhí)行l(wèi)ock操作忧设,將會(huì)清空工作內(nèi)存中此變量的值,在執(zhí)行引擎中使用這個(gè)變量前需要重新執(zhí)行l(wèi)oad或assign操作初始化變量的值
8.如果一個(gè)變量事先沒有被lock操作鎖定颠通,則不允許他執(zhí)行unlock操作址晕,也不允許去unlock一個(gè)被其他線程鎖定的變量
9.對(duì)一個(gè)變量執(zhí)行unlock操作之前,必須先把此變量同步到主內(nèi)存中(執(zhí)行store和write操作)