日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

JVM——CPU缓存架构与Java 内存模型

發布時間:2025/3/12 java 14 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JVM——CPU缓存架构与Java 内存模型 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

導航

  • 一、CPU緩存架構與一致性協議
    • 1.1 CPU緩存架構
    • 1.2 緩存行與偽共享問題
    • 1.3 MESI 緩存一致性協議
    • 1.4 偽共享的解決辦法
  • 二、JMM Java 內存模型
    • 2.1 JMM 簡介
    • 2.2 原子性、可見性、有序性
    • 2.3 八大內存交互操作
    • 2.4 happens-before 原則

一、CPU緩存架構與一致性協議

1.1 CPU緩存架構

現代 CPU 的發展非常快,內存的速度已經完全跟不上。如果將CPU完成一個基本操作所用的時間定義為時鐘周期,那么 CPU 的指令處理速度要比內存的加載速度快100 倍左右。

為了解決這個性能上的鴻溝,現代 CPU 架構往往采用如下圖所示的緩存架構:

在多核CPU和主存(Main Memory)之間引入三級高速緩存——L1、L2、L3

越靠近CPU的緩存,成本造價越高、性能越強、存儲空間越小,其中 L3 緩存是多核共享,而L1、L2 是核內私有。

1.2 緩存行與偽共享問題

緩存行 Cache Line 是高速緩存一次讀取內存數據的最小單位。目前最常見的 Intel cpu 的緩存行大小是 64 Bytes。
連續的數據很有可能由于數據跨度恰好在一個緩存行內,就很有可能會被CPU加載到 L1、L2、L3 高速緩存中。

由于高速緩存的存在,引出了緩存一致性協議,它可以保證 L1、L2、L3 中的數據在多核CPU和并發場景下不會出現線程不一致問題。早期的解決方案并不是各種緩存一致性方案,而是采用總線鎖的方式。

總線鎖,顧名思義就是將高速緩存與主內存之間的總線上鎖,只允許一個線程去獲取并操作上鎖數據,處理完成后釋放鎖,并將數據從緩存中刷回主存。這種方式雖然安全,但也犧牲了很大的性能。于是,人們又引入了諸如 MESI 的緩存一致性協議。簡單的說,就是給緩存數據做標記,如果CPU發現緩存的數據失效了,就必須從主存中重新加載最新的數據。

那什么又是偽共享呢?

偽共享是緩存行加載數據時必然會存在的性能損耗問題。當對象中包含線程局部變量,且尺寸大小小于 64 字節,就有可能發生偽共享問題。

再具體點,內存地址連續的不同變量被加載到了同一個緩存行中,而同一個緩存行中的多個變量,又不一定是CPU所需的,這種情況就是偽共享。

由于偽共享的存在,CPU核心中的高速緩存加載了本不需要數據,又在“緩存一致性協議”的要求下,不得不在這些無用數據失效后重新加載緩存,導致緩存失效,或造成性能損耗:

如上圖所示,x、y 兩個變量由 Main Memory 加載到 core 1 和 core 2兩個核心的 L1、L2 緩存中。如果線程A、B 跑在兩個核心上,且線程 A 修改 core 1 中的 x,由于緩存一致性協議, core 2 中的 x 變量將會失效,它必須從主內存中重新加載,這樣頻繁的加載、訪問主內存, core 2 中的 L1、L2 緩存幾乎等于失效。

1.3 MESI 緩存一致性協議

MESI是緩存鎖的實現方式之一,注意這是 CPU 緩存一致性,并非通常說的應用緩存。有些無法被緩存的數據或跨多個緩存行的數據依然必須使用總線鎖。

MESI 的核心思想是為每個 Cache Line 標記 4 種狀態:

  • 若緩存數據更改過,則將 Cache Line 標記為 M
  • 如果緩存數據是獨享,則標記為 E
  • 如果數據是被多個CPU讀取,則標記為 S
  • 如果數據被其他CPU修改過,則標記為 I
  • 1.4 偽共享的解決辦法

    Java 7 之前可以采用字節填充的方式,例如針對不同操作系統和對象頭的大小,補齊多個 long 類型的空數據。
    但這種方式在 Java 7 的某個版本中會出現 填充失效問題,原因是該版本的虛擬機優化了未使用 field 的排布。

    在 Java 8 加入 @Contended 注解會幫助增加128 字節的 padding,并且需要開啟 -XX:-RestrictContended 選項才能生效。

    雖然解決了偽共享的問題,但是這種填充的方式也浪費了緩存資源,而且緩存又小又貴,時間和空間的取舍要酌情考慮。

    二、JMM Java 內存模型

    2.1 JMM 簡介

    JMM 全稱是 “Java Memory Model”,Java 內存模型。
    因為在不同的硬件生產商和操作系統下,內存的訪問方式各有所差異,這樣就會造成相同的代碼出現不一樣的問題,而 JMM 屏蔽掉了各種操作系統的內存訪問差異,以實現“Write Once,Run Anywhere”的目標

    JMM 中規定所有的變量都存儲在主內存 (Main Mem)中,包括實例變量、靜態變量,但是不包括局部變量和方法參數。每條線程都有自己的工作內存(Work Mem),線程私有。工作內存中保存的是線程的變量從主內存中的拷貝副本

    這種結構的基本工作方式是:線程對變量的讀和寫都必須在工作內存中進行,而線程之間變量值的傳遞均需要通過主內存來完成。如下圖所示,注意與Java 內存結構(堆棧等)等概念區分開。

    2.2 原子性、可見性、有序性

    整個 JMM 實際上是圍繞著三個并發特征建立起來的——原子性、可見性、有序性

  • 原子性:和事務的 ACID 的 原子性概念一致,即表示一個操作中間不可分割,不能中斷,執行過程不允許被其他線程打擾。 JMM 只能保證基本操作的原子性,如果要保證一個代碼塊的原子性,Java 提供了 synchronized 關鍵字,它對應了 monitorentrer 和 monitorexit 字節碼指令。
  • 可見性:不同的線程對數據的修改結果可以被其他線程感知到,這就是可見性。synchronized 是保證可見性的最常用的操作,除此之外,還有 volatile 關鍵字,它是較弱的同步機制。
  • 有序性:字節碼指令的執行順序不可重排。可以使用 synchronized 或 volatile 保證多線程之間操作的有序性。volatile是使用內存屏障達到禁止指令重排,保證有序性。而 synchronized 則以互斥鎖的形式要求上鎖的資源必須按序執行。
  • 2.3 八大內存交互操作


    說是 8 種內存交互操作:

    • lock 和 unlock:鎖定與解鎖。作用于主內存的變量,將其設置為線程獨占或解除。
    • Read 和 Write:發生在主內存和工作內存之間,將變量傳輸到工作內存,將從工作內存得到的值放入主內存中。
    • Load 和 Store:作用于工作內存的變量,將工作內存中的變量放入副本中,將工作內存中的變量傳輸到主內存中。
    • Use 和 Assign:將工作內存中的變量傳輸到執行引擎,將一個從執行引擎中接收到的值賦值給工作內存的副本。

    使用規則:

    • 不允許 read、load、store、write 操作之一單獨出現,即 read 操作后必須 load,store后必須 write。
    • 不允許線程丟棄它最近的 assign 操作,即工作內存中的變量修改之后,必須告知主存。
    • 不允許線程將沒有 assign 的數據從工作內存同步到主存。
    • lock 和 unlock 必須成對出現。
    • 新的變量必須由主存中誕生。
    • 如果對一個變量進行 lock,會清空所有工作內存中此變量的值。在執行引擎使用這個變量前,必須重新 load 或 assign 。
    • unlock 之前,必須將此變量同步回主內存。

    2.4 happens-before 原則

    happens-before是JMM提供的有序性保證。在理解它的概念之前,需要了解一下弱內存模型強內存模型

    弱內存模型——weak memory model
    弱內存模型中,loadload、loadstore、storestore、storeload 四種內存重排序都有可能發生。只要不改變單線程行為,弱內存模型可隨意對代碼進行重排序,這是一種近乎不存在任何保證的模型。

    強內存模型
    Java的內存模型屬于一種叫做 “順序一致性” 的強內存模型。在順序一致性模型中,不再存在重排序,Java的JMM就屬于這種強內存模型。

    雖然 JMM 屬于最高級別的強內存模型——順序一致性內存模型,但并不是說Java 不允許重排序,而是在某些情況下,可以支持嚴格限制重排序的目的,如volatile。而弱內存模型是無法滿足這種保證的。

    happens-before 是 JMM 提供的一系列關于重排序問題的規則。可以概括為:

    If one action happens-before another, then the first is visible to and ordered before the second.
    如果一個動作 happens-before 另一個動作,那么前者的執行會排在后者的前面,并且(其結果)對后者可見。
    《java language specification 8》

    可以分為以下兩種策略:

  • 對于會改變程序執行結果的重排序,JMM要求編譯器和處理器必須禁止這種重排序。
  • 對于不會改變程序執行結果的重排序,JMM對編譯器和處理器不做要求(JMM允許這種重排序)。
  • 這兩個策略概括起來,可以這樣理解——只要不改變程序的執行結果,編譯器和處理器想怎么優化就怎么優化

    例如,如果編譯器經過細致的分析,認定一個鎖只能被一個線程訪問到,那么這個鎖可以被消除。
    又例如,編譯器認定一個 volatile 變量只會被單個線程訪問到,那么它可以把這個 volatile 變量當做一個普通變量對待。

    happens-before 的具體規則如下:

  • 程序順序執行,如果x和y是同一個線程的動作,并且在程序順序中x出現在y之前,則 x happens-before y。
  • 如果動作 x 的結果需要同步給后面的動作 y ,那么 x happens-before y。也就是說,synchronized 修飾的情況,先獲得鎖的線程就是 x ,后獲得鎖的就是 y,前者的執行結果需要同步給 y,那么同樣會有 x happens-before y。
  • 傳遞性,x happens-before y,y happens-before z,那么 x happens-before z。
  • 根據這些規則,以下這些場景都是 happens-before 的情況:

  • 對象鎖的解鎖,發生在對象鎖后續的上鎖之前
  • 對 volatile 變量的寫操作,一定發生在后續對該 volatile 變量的讀操作之前。
  • 對線程的 start() 方法的調用,一定發生在線程內其他操作之前。
  • 線程中的所有操作,一定發生在該線程 join() 方法結束之前。
  • 任何對象的默認初始化一定發生在程序中其他操作之前。
  • 總之,happens-before 定義了兩個動作發生資源競爭時的時間順序,是JMM 描述特定動作前后執行順序的一種抽象關系。它原則上不允許重排序,但在一些不影響程序執行結果的情況下,亂序執行也是可以的。

    總結

    以上是生活随笔為你收集整理的JVM——CPU缓存架构与Java 内存模型的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。