2017-07-17 53 views
1

我書面方式這篇文章中的連接上下文的JIT產生的x86輸出到Deep understanding of volatile in Java分析中的揮發性

public class Main { 
    private int x; 
    private volatile int g; 


    public void actor1(){ 
     x = 1; 
     g = 1; 
    } 


    public void actor2(){ 
     put_on_screen_without_sync(g); 
     put_on_screen_without_sync(x); 
    } 
} 

現在,我分析了上面這段代碼生成了什麼JIT。從我們在我以前的帖子討論中,我們知道,輸出1, 0是不可能的,因爲:


寫揮發性v使每一個動作a前述v導致該a將是可見的(將被刷新到內存)v之前將可見。


.................(I removed not important body of method)..... 

    0x00007f42307d9d5e: c7460c01000000  (1) mov  dword ptr [rsi+0ch],1h 
               ;*putfield x 
               ; - package.Main::[email protected] (line 14) 

    0x00007f42307d9d65: bf01000000   (2) mov  edi,1h 
    0x00007f42307d9d6a: 897e10    (3) mov  dword ptr [rsi+10h],edi 
    0x00007f42307d9d6d: f083042400   (4) lock add dword ptr [rsp],0h 
               ;*putfield g 
               ; - package.Main::[email protected] (line 15) 

    0x00007f42307d9d72: 4883c430   add  rsp,30h 
    0x00007f42307d9d76: 5d     pop  rbp 
    0x00007f42307d9d77: 850583535116  test  dword ptr [7f4246cef100h],eax 
               ; {poll_return} 
    0x00007f42307d9d7d: c3     ret 

難道我理解正確的話,它的作品,因爲86不能讓StoreStore重新排序?如果可能的話,將需要額外的內存屏障,是嗎?


EDITED優秀@尤金的答案AFTER:

int tmp = i; // volatile load 
// [LoadStore] 
// [LoadLoad] 

在這裏,我看你是什麼均值很明顯:every action below (after)性讀(int tmp = i)不會重新排序。

// [StoreLoad] -- this one 
int tmp = i; // volatile load 
// [LoadStore] 
// [LoadLoad] 

在這裏,你放多了一個障礙。它確保我們不會對int tmp = i重新排序。但是,爲什麼它很重要?爲什麼我有疑問?從我所知道的volatile load保證:

每個行動揮發性負載將不會被重新排序之前,易失性負載是可見的。

我看你寫的:

需要有一個順序一致性

但是,我不明白爲什麼需要順序一致性。

+0

什麼'a'?什麼'V'?你的意思是'x'和'g'? – Andreas

+0

現在,'a'是'v'上的任何動作 - 例如它是一個動作:'x = 1'。 'v'是一個商店:'g = 1' – Gilgamesz

+0

JMM不是爲x86或任何其他特定的體系結構而製作的,並且在加載或存儲方面沒有任何理由。 JVM有責任按照每個體系結構上的指令來實現JMM。 – assylias

回答

3

一些事情,第一個will be flushed to memory - 這是非常錯誤的。它幾乎從來沒有與主內存衝突 - 它通常會將StoreBuffer消耗到L1,如果您更容易理解這些概念,則可以使用緩存一致性協議來同步所有緩存之間的數據,即但是,這很好 - 只是知道這有點不同而且速度更快。

這是一個很好的問題,爲什麼[StoreLoad]確實存在,可能這會清理一些事情。 volatile確實是圍欄。下面是會發生什麼一個例子:

int tmp = i; // volatile load 
    // [LoadStore] 
    // [LoadLoad] 

說了volatile load發生的事情及以下是易失性存儲器會發生什麼:

// [StoreStore] 
// [LoadStore] 
i = tmp; // volatile store 

這不是它,還有更多。需要有一個sequential consistency,這就是爲什麼任何理智的實施將保證揮發性本身不會重新排序,從而兩個圍欄插入:

// [StoreLoad] -- this one 
int tmp = i; // volatile load 
// [LoadStore] 
// [LoadLoad] 

還有一此:

// [StoreStore] 
// [StoreLoad] 
i = tmp; // volatile store 
// [StoreLoad] -- and this one 

現在,事實證明,在x86 4個內存障礙中有3個是免費的 - 因爲它是一個strong memory model。唯一需要執行的是StoreLoad

通常mfenceStoreLoadx86一個不錯的選擇,但同樣的事情通過lock add保證,這就是爲什麼你看到它在那裏。基本上StoreLoad的障礙。是的 - 你的最後一句話是正確的,對於較弱的記憶模型 - StoreStore障礙將是必需的。在一個側面註釋中,這是在通過構造函數內的final字段安全發佈引用時使用的。在退出構造器後,插入兩個柵欄:​​和StoreStore

編輯

假設你有這樣的情況:

[StoreStore] 
[LoadStore] 
int x = i; // volatile store 

int j = 3; // plain actions 

j = x; // volatile load 
[LoadLoad] 
[LoadStore] 

基本上沒有什麼障礙會阻止volatile store重新排序與volatile load(即:揮發性負荷會首先執行)並且這會明顯地引起問題;順序一致性因此受到侵犯。

你有點錯過了這裏btw(如果我沒有弄錯)通過Every action after volatile load won't be reordered before volatile load is visible。重新排序不可能本身具有易失性 - 其他操作可以自由重新排序。我給大家舉一個例子:

int y = 0; 
int tmp = i; // volatile load 
// [LoadStore] 
// [LoadLoad] 

int x = 3; // plain load 
y = 4; // plain store 

最後兩個操作x = 3y = 4都是完全免費的重新排序,他們不能浮於揮發性以上,但通過它們本身可以重新排序。上面的例子將是完全合法的:

int y = 0; 
int tmp = i; // volatile load 
// [LoadStore] 
// [LoadLoad] 

// see how they have been inverted here... 
y = 4; // plain store 
int x = 3; // plain load 
+0

感謝您的令人印象深刻的答案。我編輯了我的帖子並詢問了一個細節。 – Gilgamesz

+0

@Gilgamesz編輯 – Eugene

+0

非常感謝! :) – Gilgamesz