源**到最終執行指令過程中,包括了多次的指令重排序
(1)編譯器重排序
1(2)處理器重排序:指令級重排序//優化前
2int x = 1;
3int y = 2;
4int a1 = x * 1;
5int b1 = y * 1;
6int a2 = x * 2;
7int b2 = y * 2;
8int a3 = x * 3;
9int b3 = y * 3;
1011
//優化後
12int x = 1;
13int y = 2;
14int a1 = x * 1;
15int a2 = x * 2;
16int a3 = x * 3;
17int b1 = y * 1;
18int b2 = y * 2;
19int b3 = y * 3;
2021
優化前的**:交替的讀x、y,會導致暫存器頻繁的交替儲存x和y,最糟的情況下暫存器要儲存3次x和3次y;
22 優化後的**:讓x的一系列操作一塊做完,y的一塊做完,理想情況下暫存器只需要儲存1次x和1次y。
1 ldr r1, [r0];//(3)處理器重排序:記憶體系統重排序操作12 add r2, r1, r1;//
操作23 add r3, r4, r4;//
操作34
5處理器在執行時往往會因為一些限制而等待,如訪存的位址不在cache中發生miss,這時就需要到記憶體甚至外存去取,然而記憶體和外區的讀取速度比cpu執行速度慢得多;
6 對於上面這段彙編**,操作1如果發生cache miss,則需要等待讀取記憶體外存。看看有沒有能優先執行的指令,操作2依賴於操作1,不能被優先執行,操作3不依賴1和2,所以能優先執行操作3,所以實際執行順序是3>1>2。這裡打破了程式執行的有序性。
1初始化:
2 a = 0;
3 b = 0;45
處理器a執行
6 a = 1; //
a17 read(b); //a28
9處理器b執行
理論執行順序:以處理器a為例,a=1 應該先執行,x=b 後執行,根據記憶體模型的效果來看a=1需要執行a1和a3,x=b則需要執行a2,所以正確的執行順序應該是a1-a3-a2
實際執行順序:因為從a處理器的角度看,a=1和x=b的執行順序先後並無影響,即a2先於a3執行並無問題。因此實際的執行順序是a1-a2-a3
a1-b2-a3的過程中,由於記憶體模型中的本地快取,導致a執行緒的寫操作無法立即被b執行緒給看到,打破了可見性。
由於處理器有讀、寫快取區,寫快取區沒有及時重新整理到記憶體
,造成其他處理器讀到的值不是最新的,使得處理器執行的讀寫操作與記憶體上反應出的順序不一致。
(4)重排序的影響
不論哪種重排序都可能造成共享變數中線程間不可見,這會改變程式執行結果。所以需要禁止對那些要求可見的共享變數重排序。
(1)阻止編譯重排序
禁止編譯器在某些時候重排序
(2)阻止指令重排序和記憶體系統重排序(使用記憶體屏障或lock字首指令)
當第二個操作是volatile寫時,不管第乙個操作是什麼,都不能重排序。這個規則確保 volatile寫之前的操作不會被編譯器重排序到volatile寫之後。
當第乙個操作是volatile讀時,不管第二個操作是什麼,都不能重排序。這個規則確保 volatile讀之後的操作不會被編譯器重排序到volatile讀之前。
當第乙個操作是volatile寫,第二個操作是volatile讀時,不能重排序。
在volatile中,所插入的記憶體屏障將不允許 volatile 字段寫操作之前的記憶體訪問被重排序至其之後;也將不允許 volatile 字段讀操作之後的記憶體訪問被重排序至其之前。
volatile保證了有序性和可見性。在未新增volatile之前,寫操作包含了寫快取-寫記憶體這兩步。在新增了volatile之後,寫操作是乙個單獨的操作,不可分割。所以記住一點,volatile保證的是多執行緒下面讀寫操作這個粒度的原子性,例如下圖中新增了鎖的set和get方法。
但volatile無法保證多執行緒下粒度更大的原子操作,例如i++
i++包括了讀記憶體、自加、寫記憶體三個步驟,但是這三個操作的原子性無法保證
//處理器b執行
jvm記憶體模型 JVM記憶體模型詳情解析
一 結構圖 note string常量池 存在 堆記憶體中 二 各部分詳情解析 1 堆1 老年代 物件年齡 經過一次 monitor gc 年齡加1 15 的會存到 老年代 2 年輕代 3 常量池 string常量 儲存在堆中 2 虛擬機器棧 1 區域性變數表 2 運算元棧 3 動態鏈結 4 方法出...
jvm記憶體模型
主要分為棧,堆,方法區,程式計數器 1.程式計數器 2.棧 stack 虛擬機器棧 每個執行緒獨生成乙個棧,執行緒中每呼叫乙個方法生成乙個棧幀,棧幀依次壓棧 棧幀中存放了每個方法的基本資料變數,物件的引用,操作指令,出口資訊等 本地方法棧 存放的是native方法 其他語言寫的 其他和虛擬機器棧一樣...
jvm記憶體模型
補充 可見性 乙個執行緒修改了變數,其他執行緒可以立即知道 保證可見性的方法 volatile synchronized unlock之前,寫變數值回主存 final 一旦初始化完成,其他執行緒就可見 有序性 在本執行緒內,操作都是有序的 重排或 主記憶體同步延時 指令重排 執行緒 內序列語義 寫後...