Tungsten Fabric SDN — SmartNIC vRouter
858
2022-05-29
在了解Java內(nèi)存模型之前,先來(lái)看一下多核硬件架構(gòu)。
我們應(yīng)該都知道,計(jì)算機(jī)在執(zhí)行程序的時(shí)候,每條指令都是在CPU中執(zhí)行的,而執(zhí)行的時(shí)候,又免不了要和數(shù)據(jù)打交道。而計(jì)算機(jī)上面的數(shù)據(jù),是存放在主存當(dāng)中的,也就是計(jì)算機(jī)的物理內(nèi)存啦。
剛開(kāi)始,還相安無(wú)事的,但是隨著CPU技術(shù)的發(fā)展,CPU的執(zhí)行速度越來(lái)越快。而由于內(nèi)存的技術(shù)并沒(méi)有太大的變化,所以從內(nèi)存中讀取和寫(xiě)入數(shù)據(jù)的過(guò)程和CPU的執(zhí)行速度比起來(lái)差距就會(huì)越來(lái)越大,這就導(dǎo)致CPU每次操作內(nèi)存都要耗費(fèi)很多等待時(shí)間。
所以,人們想出來(lái)了一個(gè)好的辦法,就是在CPU和內(nèi)存之間增加高速緩存。緩存的概念大家都知道,就是保存一份數(shù)據(jù)拷貝。他的特點(diǎn)是速度快,內(nèi)存小,并且昂貴。
再隨著市場(chǎng)對(duì)CPU計(jì)算能力的需要,于是出現(xiàn)了多核CPU,每個(gè)核都有各自的緩存。下圖簡(jiǎn)單描述了多核硬件架構(gòu)的實(shí)現(xiàn)。
現(xiàn)代計(jì)算機(jī)硬件,幾乎都是多核處理器實(shí)現(xiàn)。打開(kāi)Windows的任務(wù)管理器,可以核心數(shù),還可以看到處理器的緩存。如下圖紅框:
可以清晰的看到L1、L2、L3三級(jí)緩存。
計(jì)算機(jī)內(nèi)部的緩存架構(gòu)(CPU三級(jí)緩存)
緩存大大縮小了高速CPU與低速內(nèi)存之間的差距。以三層緩存架構(gòu)為例:
L1 Cache最接近CPU, 容量最小(如32K、64K、256K等)、速度最高,每個(gè)核上都有一個(gè)L1 Cache。
L2 Cache容量更大(如256K)、速度更低, 一般情況下,每個(gè)核上都有一個(gè)獨(dú)立的L2 Cache。
L3 Cache最接近內(nèi)存,容量最大(如12MB),速度最低,在同一個(gè)CPU插槽之間的核共享一個(gè)L3 Cache。
單核時(shí)代只有一個(gè)處理器核心,讀/寫(xiě)操作完全都是由單核完成,沒(méi)什么問(wèn)題;但是多核架構(gòu),一個(gè)核修改主存后,其他核心并不知道數(shù)據(jù)已經(jīng)失效,繼續(xù)傻傻的使用,輕則數(shù)據(jù)計(jì)算錯(cuò)誤,重則導(dǎo)致死循環(huán)、程序崩潰等。
緩存一致性協(xié)議
多核CPU硬件架構(gòu)廠商,設(shè)計(jì)之初就預(yù)測(cè)到多線程操作數(shù)據(jù)不一致的問(wèn)題,因此出現(xiàn)了——緩存一致性協(xié)議。
不同的CPU硬件生產(chǎn)廠商,具體的實(shí)現(xiàn)不一樣。Intel的MESI協(xié)議最出名。
MESI協(xié)議文檔:https://en.wikipedia.org/wiki/MESI_protocol
在MESI協(xié)議中,每個(gè)Cache line有4個(gè)狀態(tài),可用2個(gè)bit表示,它們分別是:
狀態(tài) 描述
M(Modified) 這行數(shù)據(jù)有效,數(shù)據(jù)被修改了,和內(nèi)存中的數(shù)據(jù)不一致,數(shù)據(jù)只存在于本Cache中。
E(Exclusive) 這行數(shù)據(jù)有效,數(shù)據(jù)和內(nèi)存中的數(shù)據(jù)一致,數(shù)據(jù)只存在于本Cache中。
S(Shared) 這行數(shù)據(jù)有效,數(shù)據(jù)和內(nèi)存中的數(shù)據(jù)一致,數(shù)據(jù)存在于很多Cache中。
I(Invalid) 這行數(shù)據(jù)無(wú)效。
在MESI協(xié)議中,每個(gè)Cache的Cache控制器不僅知道自己的讀寫(xiě)操作,而且也監(jiān)聽(tīng)(snoop)其它Cache的讀寫(xiě)操作。每個(gè)Cache line所處的狀態(tài)根據(jù)本核和其它核的讀寫(xiě)操作在4個(gè)狀態(tài)間進(jìn)行遷移,如下圖:
AMD的Opteron處理器使用從MESI中演化出的MOESI協(xié)議,O(Owned)是MESI中S和M的一個(gè)合體,表示本Cache line被修改,和內(nèi)存中的數(shù)據(jù)不一致,不過(guò)其它的核可以有這份數(shù)據(jù)的拷貝,狀態(tài)為S。
Intel的core i7處理器使用從MESI中演化出的MESIF協(xié)議,F(xiàn)(Forward)從Share中演化而來(lái),一個(gè)Cache line如果是Forward狀態(tài),它可以把數(shù)據(jù)直接傳給其它內(nèi)核的Cache,而Share則不能。
用下面的簡(jiǎn)圖,簡(jiǎn)單說(shuō)明下MESI如何在多核環(huán)境,確保緩存一致性的。
兩個(gè)CPU執(zhí)行兩個(gè)線程,都執(zhí)行count++,都先從主內(nèi)存獲取count=0,都將count=0從主存拷貝到各自的緩存區(qū),且在緩存區(qū)count=0的狀態(tài)為S(Shared,多線程共享狀態(tài))。
一個(gè)線程(左邊的線程)先執(zhí)行了修改count+=1,按MESI緩存協(xié)議規(guī)范,在該線程中count是M狀態(tài)(即:已被修改),則其他擁有count變量的線程,count狀態(tài)都變?yōu)?I)失效狀態(tài),即需要再去主內(nèi)存拿新值。
可見(jiàn),緩存一致性協(xié)議解決了多核硬件架構(gòu)的一致性問(wèn)題。那緩存一致性又和JMM有什么關(guān)系呢?
JMM也是遵照多核硬件架構(gòu)的設(shè)計(jì),用Java實(shí)現(xiàn)了一套JVM層面的“緩存一致性”。
如果開(kāi)發(fā)者運(yùn)用的得當(dāng),最終可以達(dá)到Java線程間:變量可見(jiàn)性、有序性、原子性。
有了MESI,為什么還需要JMM?
既然有了MESI協(xié)議,是不是就不需要volatile的可見(jiàn)性語(yǔ)義了?當(dāng)然不是,還有以下問(wèn)題:
并不是所有的硬件架構(gòu)都提供了相同的一致性保證,不同的硬件廠商實(shí)現(xiàn)不同,JVM需要volatile統(tǒng)一語(yǔ)義。
可見(jiàn)性問(wèn)題不僅僅局限于CPU緩存內(nèi),JVM自己維護(hù)的內(nèi)存模型(JMM)中也有可見(jiàn)性問(wèn)題。使用volatile做標(biāo)記,可以解決JVM層面的可見(jiàn)性問(wèn)題。
Java內(nèi)存模型JMM
Java內(nèi)存模型(Java Memory Model,簡(jiǎn)稱JMM),本身是種抽象的概念,并不是像硬件架構(gòu)一樣真實(shí)存在的;它描述的是一組規(guī)則或規(guī)范,通過(guò)這組規(guī)范定義了程序中各個(gè)變量(包括實(shí)例字段、靜態(tài)字段和構(gòu)成數(shù)組對(duì)象的元素)的訪問(wèn)方式。
由于JVM運(yùn)行程序的實(shí)體是線程,而每個(gè)線程創(chuàng)建時(shí),JVM都會(huì)為其創(chuàng)建一個(gè)工作內(nèi)存(有些地方稱為棧空間),用于存儲(chǔ)線程私有數(shù)據(jù),而Java內(nèi)存模型中規(guī)定所有變量都存儲(chǔ)在主內(nèi)存,主內(nèi)存時(shí)關(guān)系內(nèi)存區(qū)域,所有線程都可以訪問(wèn),但線程對(duì)變量的操作(讀取賦值等)必須在工作內(nèi)存中進(jìn)行,首先要將變量從主內(nèi)存拷貝到自己的工作內(nèi)存空間,然后對(duì)變量進(jìn)行操作,操作完后再將變量寫(xiě)回主內(nèi)存,不能直接操作主內(nèi)存中的變量。
工作內(nèi)存是每個(gè)線程的私有數(shù)據(jù)區(qū)域,因此不同線程間無(wú)法訪問(wèn)對(duì)方的工作內(nèi)存,線程間的通信(傳值)必須通過(guò)主內(nèi)存來(lái)完成。如下圖所示:
內(nèi)存模型JMM與Java運(yùn)行時(shí)數(shù)據(jù)區(qū)的關(guān)系
Java虛擬機(jī)的運(yùn)行時(shí)數(shù)據(jù)區(qū)被劃分為:方法區(qū)、Java堆、Java虛擬機(jī)棧、PC寄存器、本地方法棧,還有常量池。它們被分為兩大類——線程共享和線程私有數(shù)據(jù)區(qū)。
線程共享數(shù)據(jù)區(qū),包括:Java堆、方法區(qū)、常量池。它們會(huì)隨著虛擬機(jī)啟動(dòng)而創(chuàng)建,隨著虛擬機(jī)退出而銷(xiāo)毀。
線程私有數(shù)據(jù)區(qū),包括:PC寄存器、JVM棧、本地方法區(qū)。它們是與線程一一對(duì)應(yīng)的,這些與線程對(duì)應(yīng)的數(shù)據(jù)區(qū)域會(huì)隨著線程開(kāi)始和結(jié)束而創(chuàng)建和銷(xiāo)毀。詳細(xì)了解可閱讀:JVM體系結(jié)構(gòu)-----深入理解內(nèi)存結(jié)構(gòu)
本質(zhì)上來(lái)說(shuō),內(nèi)存模型JMM和Java運(yùn)行時(shí)數(shù)據(jù)區(qū)沒(méi)有任何關(guān)系,它們是兩個(gè)不同緯度的東西。
但如果硬要扯上關(guān)系的話,可以把Java運(yùn)行時(shí)數(shù)據(jù)區(qū)的JVM棧 看作是內(nèi)存模型中的"工作內(nèi)存",因?yàn)榘碕ava運(yùn)行時(shí)數(shù)據(jù)區(qū)對(duì)內(nèi)存的劃分,符合線程私有性質(zhì)、且存儲(chǔ)本地變量的存儲(chǔ)區(qū)域,只有線程私有的棧空間。
JMM主內(nèi)存與工作內(nèi)存的數(shù)據(jù)存儲(chǔ)類型以及操作方式
根據(jù)虛擬機(jī)規(guī)范,對(duì)于一個(gè)實(shí)例對(duì)象中的成員方法而言
1、如果方法中包含本地變量是基本數(shù)據(jù)類型,將直接存儲(chǔ)在工作內(nèi)存(棧)中;
2、但如果本地變量是引用類型,那么該變量的引用會(huì)存儲(chǔ)在工作內(nèi)存的棧中,而對(duì)象的實(shí)例將存儲(chǔ)在主內(nèi)存(堆)中。但對(duì)于實(shí)例對(duì)象的成員變量,不管它是基本數(shù)據(jù)類型或者包裝類型(Integer、Double等)還是引用類型,都會(huì)被存儲(chǔ)到堆區(qū)。
3、至于static變量以及類自身相關(guān)信息將會(huì)存儲(chǔ)在主內(nèi)存中。
需要注意的是,在主內(nèi)存中的實(shí)例對(duì)象可以被多線程共享,如果兩個(gè)線程同時(shí)調(diào)用了同一個(gè)對(duì)象的同一個(gè)方法,那么兩個(gè)線程會(huì)將 要操作的數(shù)據(jù)拷貝一份到自己的工作內(nèi)存中,執(zhí)行完成后才刷新到主內(nèi)存,如下圖所示:
Java內(nèi)存模型 與 硬件內(nèi)存架構(gòu)的關(guān)系
對(duì)于硬件架構(gòu)來(lái)說(shuō),只有寄存器、緩存、主內(nèi)存的概念,并沒(méi)有工作內(nèi)存(線程私有數(shù)據(jù)區(qū))和主內(nèi)存(堆內(nèi)存)之分,也就是說(shuō)Java內(nèi)存模型對(duì)內(nèi)存的劃分對(duì)硬件內(nèi)存并沒(méi)有任何影響,因?yàn)镴MM是一種抽象的概念,是一組規(guī)則,并不實(shí)際存在,不管是工作內(nèi)存的數(shù)據(jù)還是主內(nèi)存的數(shù)據(jù),對(duì)于計(jì)算機(jī)硬件來(lái)說(shuō)都會(huì)存儲(chǔ)在計(jì)算機(jī)主內(nèi)存中,當(dāng)然也有可能存儲(chǔ)到CPU緩存或者寄存器中,因此總體上,Java內(nèi)存模型和硬件內(nèi)存架構(gòu)師一個(gè)相互交叉的關(guān)系,是一種抽象概念劃分與真實(shí)物理硬件的交叉。(注意對(duì)于Java內(nèi)存區(qū)域劃分也是同樣的道理)
小結(jié):
1、硬件架構(gòu)的緩存一致性協(xié)議(如MESI),解決了多核硬件的一致性問(wèn)題。
JMM也是遵照多核硬件架構(gòu)的設(shè)計(jì),用Java實(shí)現(xiàn)了一套JVM層面的“緩存一致性”。
如果開(kāi)發(fā)者運(yùn)用的得當(dāng),最終可以達(dá)到Java線程間:內(nèi)存可見(jiàn)性、有序性、原子性。
2、JMM不像硬件架構(gòu),不是實(shí)實(shí)在在存在的,JMM是理論基礎(chǔ)。但JMM最終還是要落到硬件架構(gòu)上去運(yùn)行。
存檔文章
終于有人把Java內(nèi)存模型(JMM)說(shuō)清楚了
JVM體系結(jié)構(gòu)-----深入理解內(nèi)存結(jié)構(gòu)
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。