国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專(zhuān)欄INFORMATION COLUMN

從匯編看Volatile的內(nèi)存屏障

szysky / 3033人閱讀

摘要:為了實(shí)現(xiàn)的內(nèi)存語(yǔ)義,編譯器在生成字節(jié)碼時(shí),會(huì)在指令序列中插入內(nèi)存屏障來(lái)禁止特定類(lèi)型的處理器重排序。上述寫(xiě)和讀的內(nèi)存屏障插入策略非常保守。

本講座地址https://segmentfault.com/l/15... 歡迎大家圍觀

Java的Volatile的特征是任何讀都能讀到最新值,本質(zhì)上是JVM通過(guò)內(nèi)存屏障來(lái)實(shí)現(xiàn)的,讓我們看看從字節(jié)碼以及匯編碼的角度,來(lái)看下是否真是如此?

一 Volatile與內(nèi)存屏障

本節(jié)內(nèi)容來(lái)自:http://www.infoq.com/cn/artic...

為了實(shí)現(xiàn)volatile內(nèi)存語(yǔ)義,JMM會(huì)分別限制重排序類(lèi)型。下面是JMM針對(duì)編譯器制定的volatile重排序規(guī)則表:

舉例來(lái)說(shuō),第三行最后一個(gè)單元格的意思是:在程序順序中,當(dāng)?shù)谝粋€(gè)操作為普通變量的讀或?qū)憰r(shí),如果第二個(gè)操作為volatile寫(xiě),則編譯器不能重排序這兩個(gè)操作。

從上表我們可以看出:

當(dāng)?shù)诙€(gè)操作是volatile寫(xiě)時(shí),不管第一個(gè)操作是什么,都不能重排序。這個(gè)規(guī)則確保volatile寫(xiě)之前的操作不會(huì)被編譯器重排序到volatile寫(xiě)之后。

當(dāng)?shù)谝粋€(gè)操作是volatile讀時(shí),不管第二個(gè)操作是什么,都不能重排序。這個(gè)規(guī)則確保volatile讀之后的操作不會(huì)被編譯器重排序到volatile讀之前。

當(dāng)?shù)谝粋€(gè)操作是volatile寫(xiě),第二個(gè)操作是volatile讀時(shí),不能重排序。

為了實(shí)現(xiàn)volatile的內(nèi)存語(yǔ)義,編譯器在生成字節(jié)碼時(shí),會(huì)在指令序列中插入內(nèi)存屏障來(lái)禁止特定類(lèi)型的處理器重排序。對(duì)于編譯器來(lái)說(shuō),發(fā)現(xiàn)一個(gè)最優(yōu)布置來(lái)最小化插入屏障的總數(shù)幾乎不可能,為此,JMM采取保守策略。下面是基于保守策略的JMM內(nèi)存屏障插入策略:

在每個(gè)volatile寫(xiě)操作的前面插入一個(gè)StoreStore屏障。

在每個(gè)volatile寫(xiě)操作的后面插入一個(gè)StoreLoad屏障。

在每個(gè)volatile讀操作的后面插入一個(gè)LoadLoad屏障。

在每個(gè)volatile讀操作的后面插入一個(gè)LoadStore屏障。

上述內(nèi)存屏障插入策略非常保守,但它可以保證在任意處理器平臺(tái),任意的程序中都能得到正確的volatile內(nèi)存語(yǔ)義。

下面是保守策略下,volatile寫(xiě)插入內(nèi)存屏障后生成的指令序列示意圖:

上圖中的StoreStore屏障可以保證在volatile寫(xiě)之前,其前面的所有普通寫(xiě)操作已經(jīng)對(duì)任意處理器可見(jiàn)了。這是因?yàn)镾toreStore屏障將保障上面所有的普通寫(xiě)在volatile寫(xiě)之前刷新到主內(nèi)存。

這里比較有意思的是volatile寫(xiě)后面的StoreLoad屏障。這個(gè)屏障的作用是避免volatile寫(xiě)與后面可能有的volatile讀/寫(xiě)操作重排序。因?yàn)榫幾g器常常無(wú)法準(zhǔn)確判斷在一個(gè)volatile寫(xiě)的后面,是否需要插入一個(gè)StoreLoad屏障(比如,一個(gè)volatile寫(xiě)之后方法立即return)。為了保證能正確實(shí)現(xiàn)volatile的內(nèi)存語(yǔ)義,JMM在這里采取了保守策略:在每個(gè)volatile寫(xiě)的后面或在每個(gè)volatile讀的前面插入一個(gè)StoreLoad屏障。從整體執(zhí)行效率的角度考慮,JMM選擇了在每個(gè)volatile寫(xiě)的后面插入一個(gè)StoreLoad屏障。因?yàn)関olatile寫(xiě)-讀內(nèi)存語(yǔ)義的常見(jiàn)使用模式是:一個(gè)寫(xiě)線(xiàn)程寫(xiě)volatile變量,多個(gè)讀線(xiàn)程讀同一個(gè)volatile變量。當(dāng)讀線(xiàn)程的數(shù)量大大超過(guò)寫(xiě)線(xiàn)程時(shí),選擇在volatile寫(xiě)之后插入StoreLoad屏障將帶來(lái)可觀的執(zhí)行效率的提升。從這里我們可以看到JMM在實(shí)現(xiàn)上的一個(gè)特點(diǎn):首先確保正確性,然后再去追求執(zhí)行效率。

下面是在保守策略下,volatile讀插入內(nèi)存屏障后生成的指令序列示意圖:

上圖中的LoadLoad屏障用來(lái)禁止處理器把上面的volatile讀與下面的普通讀重排序。LoadStore屏障用來(lái)禁止處理器把上面的volatile讀與下面的普通寫(xiě)重排序。

上述volatile寫(xiě)和volatile讀的內(nèi)存屏障插入策略非常保守。在實(shí)際執(zhí)行時(shí),只要不改變volatile寫(xiě)-讀的內(nèi)存語(yǔ)義,編譯器可以根據(jù)具體情況省略不必要的屏障。下面我們通過(guò)具體的示例代碼來(lái)說(shuō)明:

class VolatileBarrierExample {
    int a;
    volatile int v1 = 1;
    volatile int v2 = 2;

    void readAndWrite() {
        int i = v1;           //第一個(gè)volatile讀
        int j = v2;           // 第二個(gè)volatile讀
        a = i + j;            //普通寫(xiě)
        v1 = i + 1;          // 第一個(gè)volatile寫(xiě)
        v2 = j * 2;          //第二個(gè) volatile寫(xiě)
    }

    …                    //其他方法
}

針對(duì)readAndWrite()方法,編譯器在生成字節(jié)碼時(shí)可以做如下的優(yōu)化:

注意,最后的StoreLoad屏障不能省略。因?yàn)榈诙€(gè)volatile寫(xiě)之后,方法立即return。此時(shí)編譯器可能無(wú)法準(zhǔn)確斷定后面是否會(huì)有volatile讀或?qū)懀瑸榱税踩鹨?jiàn),編譯器常常會(huì)在這里插入一個(gè)StoreLoad屏障。

上面的優(yōu)化是針對(duì)任意處理器平臺(tái),由于不同的處理器有不同“松緊度”的處理器內(nèi)存模型,內(nèi)存屏障的插入還可以根據(jù)具體的處理器內(nèi)存模型繼續(xù)優(yōu)化。以x86處理器為例,上圖中除最后的StoreLoad屏障外,其它的屏障都會(huì)被省略。

前面保守策略下的volatile讀和寫(xiě),在 x86處理器平臺(tái)可以?xún)?yōu)化成:

前文提到過(guò),x86處理器僅會(huì)對(duì)寫(xiě)-讀操作做重排序。X86不會(huì)對(duì)讀-讀,讀-寫(xiě)和寫(xiě)-寫(xiě)操作做重排序,因此在x86處理器中會(huì)省略掉這三種操作類(lèi)型對(duì)應(yīng)的內(nèi)存屏障。在x86中,JMM僅需在volatile寫(xiě)后面插入一個(gè)StoreLoad屏障即可正確實(shí)現(xiàn)volatile寫(xiě)-讀的內(nèi)存語(yǔ)義。這意味著在x86處理器中,volatile寫(xiě)的開(kāi)銷(xiāo)比volatile讀的開(kāi)銷(xiāo)會(huì)大很多(因?yàn)閳?zhí)行StoreLoad屏障開(kāi)銷(xiāo)會(huì)比較大)。

二 Volatile的字節(jié)碼

為了搞清楚內(nèi)存屏障,我們扒開(kāi)class字節(jié)碼看一下,用javap -v -p class文件名(不要.class 后綴)運(yùn)行

volatile int v1;
    descriptor: I
    flags: ACC_VOLATILE
   .....
void readAndWrite();
    descriptor: ()V
    flags:
    Code:
      stack=3, locals=3, args_size=1
         0: aload_0
         1: getfield      #52                 // Field v1:I
         4: istore_1
         5: aload_0
         6: getfield      #54                 // Field v2:I
         9: istore_2
        10: aload_0
        11: iload_1
        12: iload_2
        13: iadd
        14: putfield      #72                 // Field a:I
        17: aload_0
        18: iload_1
        19: iconst_1
        20: isub
        21: putfield      #52                 // Field v1:I
        24: aload_0
        25: iload_2
        26: iload_1
        27: imul
        28: putfield      #54                 // Field v2:I
        31: return

除了其變量定義的時(shí)候有一個(gè)Volatile外,之后的字節(jié)碼跟有無(wú)Volatile完全一樣,于是我們又扒了下匯編代碼

三 Volatile的匯編碼

為了看到匯編碼,要使用hsdis插件, 在mac系統(tǒng)下需要安裝一個(gè)hsdis-amd64.dylib的插件。在網(wǎng)上找了一個(gè),地址在這里。
下載下來(lái)后,將其放置到你的jre lib目錄下即可。
mac系統(tǒng)上命令如下,

sudo mv ./hsdis-amd64.dylib  /Library/Java/JavaVirtualMachines/jdk1.8.0_31.jdk/Contents/Home/jre/lib

然后再運(yùn)行

java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -Xcomp -XX:CompileCommand=dontinline,*VolatileBarrierExample.readAndWrite -XX:CompileCommand=compileonly,*VolatileBarrierExample.readAndWrite com.earnfish.VolatileBarrierExample > out.put

其中*VolatileBarrierExample.readAndWrite表示你運(yùn)行的類(lèi).函數(shù), com.earnfish.VolatileBarrierExample表示你的包名.類(lèi)名,注意需要有main函數(shù)來(lái)運(yùn)行你所要執(zhí)行的函數(shù)。得出匯編碼如下

  0x000000011214bb49: mov    %rdi,%rax
  0x000000011214bb4c: dec    %eax
  0x000000011214bb4e: mov    %eax,0x10(%rsi)
  0x000000011214bb51: lock addl $0x0,(%rsp)     ;*putfield v1
                                                ; - com.earnfish.VolatileBarrierExample::readAndWrite@21 (line 35)

  0x000000011214bb56: imul   %edi,%ebx
  0x000000011214bb59: mov    %ebx,0x14(%rsi)
  0x000000011214bb5c: lock addl $0x0,(%rsp)     ;*putfield v2
                                                ; - com.earnfish.VolatileBarrierExample::readAndWrite@28 (line 36)

其對(duì)應(yīng)的Java代碼如下

 v1 = i - 1;          // 第一個(gè)volatile寫(xiě)
 v2 = j * i;          // 第二個(gè)volatile寫(xiě)

可見(jiàn)其本質(zhì)是通過(guò)一個(gè)lock指令來(lái)實(shí)現(xiàn)的。那么lock是什么意思呢?

查詢(xún)IA32手冊(cè),它的作用是使得本CPU的Cache寫(xiě)入了內(nèi)存,該寫(xiě)入動(dòng)作也會(huì)引起別的CPU invalidate其Cache。所以通過(guò)這樣一個(gè)空操作,可讓前面volatile變量的修改對(duì)其他CPU立即可見(jiàn)。

所以,它的作用是

鎖住主存

任何讀必須在寫(xiě)完成之后再執(zhí)行

使其它線(xiàn)程這個(gè)值的棧緩存失效

類(lèi)似于前面是storestore,后面是storeload

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/69025.html

相關(guān)文章

  • volatile,可見(jiàn)性,有序性

    摘要:內(nèi)存語(yǔ)義的的實(shí)現(xiàn)可見(jiàn)性的實(shí)現(xiàn)基于的讀取,寫(xiě)入兩個(gè)操作的內(nèi)存語(yǔ)義。首先,對(duì)中內(nèi)存屏障的介紹內(nèi)存屏障用于控制特定條件下的重排序和內(nèi)存可見(jiàn)性問(wèn)題。在大多數(shù)處理器的實(shí)現(xiàn)中,這個(gè)屏障是個(gè)萬(wàn)能屏障,兼具其它三種內(nèi)存屏障的功能。 volatile,可見(jiàn)性,有序性 volatile的特性 可見(jiàn)性:對(duì)一個(gè)volatile變量的讀,總能獲取其他任意線(xiàn)程對(duì)該變量最后的寫(xiě)入。 有序性:JMM會(huì)限制volat...

    caige 評(píng)論0 收藏0
  • (七)Volatile作用及原理

    摘要:文章簡(jiǎn)介分析的作用以及底層實(shí)現(xiàn)原理,這也是大公司喜歡問(wèn)的問(wèn)題內(nèi)容導(dǎo)航的作用什么是可見(jiàn)性源碼分析的作用在多線(xiàn)程中,和都起到非常重要的作用,是通過(guò)加鎖來(lái)實(shí)現(xiàn)線(xiàn)程的安全性。而的主要作用是在多處理器開(kāi)發(fā)中保證共享變量對(duì)于多線(xiàn)程的可見(jiàn)性。 文章簡(jiǎn)介 分析volatile的作用以及底層實(shí)現(xiàn)原理,這也是大公司喜歡問(wèn)的問(wèn)題 內(nèi)容導(dǎo)航 volatile的作用 什么是可見(jiàn)性 volatile源碼分析 ...

    marek 評(píng)論0 收藏0
  • volatile詳解

    摘要:內(nèi)存模型基本概念計(jì)算機(jī)在執(zhí)行程序時(shí),每條指令都是在中執(zhí)行的,而執(zhí)行指令過(guò)程中,勢(shì)必涉及到數(shù)據(jù)的讀取和寫(xiě)入。有序性即程序執(zhí)行的順序按照代碼的先后順序執(zhí)行。 內(nèi)存模型基本概念 計(jì)算機(jī)在執(zhí)行程序時(shí),每條指令都是在CPU中執(zhí)行的,而執(zhí)行指令過(guò)程中,勢(shì)必涉及到數(shù)據(jù)的讀取和寫(xiě)入。由于程序運(yùn)行過(guò)程中的臨時(shí)數(shù)據(jù)是存放在主存(物理內(nèi)存)當(dāng)中的,這時(shí)就存在一個(gè)問(wèn)題,由于CPU執(zhí)行速度很快,而從內(nèi)存讀取數(shù)據(jù)...

    aikin 評(píng)論0 收藏0
  • Java并發(fā)編程,3分分鐘深入分析volatile實(shí)現(xiàn)原理

    摘要:一言以蔽之,被修飾的變量能夠保證每個(gè)線(xiàn)程能夠獲取該變量的最新值,從而避免出現(xiàn)數(shù)據(jù)臟讀的現(xiàn)象。為了實(shí)現(xiàn)內(nèi)存語(yǔ)義時(shí),編譯器在生成字節(jié)碼時(shí),會(huì)在指令序列中插入內(nèi)存屏障來(lái)禁止特定類(lèi)型的處理器重排序。volatile原理volatile簡(jiǎn)介Java內(nèi)存模型告訴我們,各個(gè)線(xiàn)程會(huì)將共享變量從主內(nèi)存中拷貝到工作內(nèi)存,然后執(zhí)行引擎會(huì)基于工作內(nèi)存中的數(shù)據(jù)進(jìn)行操作處理。 線(xiàn)程在工作內(nèi)存進(jìn)行操作后何時(shí)會(huì)寫(xiě)到主內(nèi)存中...

    番茄西紅柿 評(píng)論0 收藏0
  • Java并發(fā)編程,3分分鐘深入分析volatile實(shí)現(xiàn)原理

    摘要:一言以蔽之,被修飾的變量能夠保證每個(gè)線(xiàn)程能夠獲取該變量的最新值,從而避免出現(xiàn)數(shù)據(jù)臟讀的現(xiàn)象。為了實(shí)現(xiàn)內(nèi)存語(yǔ)義時(shí),編譯器在生成字節(jié)碼時(shí),會(huì)在指令序列中插入內(nèi)存屏障來(lái)禁止特定類(lèi)型的處理器重排序。volatile原理volatile簡(jiǎn)介Java內(nèi)存模型告訴我們,各個(gè)線(xiàn)程會(huì)將共享變量從主內(nèi)存中拷貝到工作內(nèi)存,然后執(zhí)行引擎會(huì)基于工作內(nèi)存中的數(shù)據(jù)進(jìn)行操作處理。 線(xiàn)程在工作內(nèi)存進(jìn)行操作后何時(shí)會(huì)寫(xiě)到主內(nèi)存中...

    番茄西紅柿 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<