JVM實驗四 記憶體模型和volatile

2022-09-06 15:06:14 字數 3060 閱讀 8428

源**到最終執行指令過程中,包括了多次的指令重排序

(1)編譯器重排序

1

//優化前

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。

(2)處理器重排序:指令級重排序

1 ldr r1, [r0];//

操作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。這裡打破了程式執行的有序性。

(3)處理器重排序:記憶體系統重排序

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 一旦初始化完成,其他執行緒就可見 有序性 在本執行緒內,操作都是有序的 重排或 主記憶體同步延時 指令重排 執行緒 內序列語義 寫後...