1. 程式人生 > >Java內存 模型理解

Java內存 模型理解

其他 數據對象 另一個 虛擬機 不一致 .com 這樣的 span 元素

概述

  在正式講Java內存模型之前,我們先了解一些物理計算機並發問題,然後一點點的引出Java內存模型的由來。

  多任務處理在現在計算機操作系統中幾乎是一項必備的功能。這不單是因為計算機計算能力強大,更重要的原因是計算機的計算速度遠高於它的的存儲和通信子系統速度。所以我們就通過讓計算機同時處理多個任務來講處理器的運算能力得到充分運用。

  除了充分運用計算機的處理能力外,一個服務端同時對多個客戶端提供服務則是另一個更具體的並發應用的場景。衡量一個服務性能的高低好壞,每秒事務處理數(TPS)是一個重要指標,它代表著一秒內服務端平均能響應的請求總數,而TPS值和程序的並發能力又有非常密切的關系。對於計算量相同的任務,程勛線程的並發協調性越有條不紊,效率自然就會越高;反之,線程之間頻繁的阻塞甚至死鎖,將會大大降低程序的並發能力。

  在了解Java並發問題之前我們先了解一下,物理計算機的並發問題,物理機遇到的並發問題和虛擬機中的情況有喝多相似的地方,物理機對並發的處理方案對於虛擬機有很大的參考意義。

  前面說過,為了更充分的利用處理器的性能,我們讓計算機並發執行多個運算任務,這種因果關系看起來順理成章。但是他們的其實並沒有這麽簡單,因為絕大多數的運算都不可能只靠處理器,處理器至少要和內存進行交互,如讀物運算數據,存儲運算結果等,這個I/O操作時很難消除的(無法緊靠寄存器來完成所有的運算任務)。所以現在計算機都會加入一層讀寫速度盡可能接近處理器運算速度的“高速緩存”,來作為處理器和內存之間的緩沖:將運算需要的數據復制到緩存中,讓運算能快速的進行,當運算結束後從緩存同步回內存之中,這樣處理器就不用等待緩慢的內存讀寫了。

  高速緩存很好的解決了處理器和內存的速度矛盾,但是這也為計算機系統帶來了更高的復雜度,因為它引起了一個新的問題:緩存一致性。在多處理器系統中,每個處理器都有自己的高速緩存,二他們有共享一個主內存。當多個處理器的運算任務逗哦設計到同一塊主內存區域時,將可能導致各自的緩存數據不一致。如果真的發生了緩存不一致的問題,那同步回到主內存時以誰的緩存數據為準呢?為了解決緩存一致性問題,需要各個處理器在訪問緩存時都遵守一些協議,在讀寫時根據這些協議來進行操作。而在本文中要討論的內存模型可以理解為在特定的操作協議下對特定的內存胡哦哦哦告訴緩存進行的讀寫訪問的過程抽象。不同架構的物理機可以擁有不一樣的內存模型,java虛擬機也有自己的內存模型。

  除了增加告訴緩存,為了使處理器內部的運算單元能盡量的被充分利用,處理器可能會對輸入代碼進行亂序執行優化,處理器會在計算之後將亂序執行的結果重組,保證該結果和順序執行的結果是一致的。因此,如果哦存在一個計算任務以來另一個計算任務的中間結果,那麽氣順序性並不能卡哦哦代碼的先後順序來保證。其實java虛擬機中指令重拍優化也是類似的優化。

為什麽要定義Java內存模型

  java虛擬機規範中試圖定義一種java內存模型(JMM)來屏蔽掉各種硬件和操作系統的內存訪問差異,一實現讓java程序在各種平臺下都可能達到一致的內存訪問效果。在此之前,C語音/C++直接使用物理硬件和操作系統的內存模型,所以就會出現在一套平臺上並發訪問正常,但是在另一套平臺上卻有問題,平臺兼容性相對較差。

Java內存模型的目的及實現方式

   JMM的主要目標是定義程序中的各個變量的訪問規則,即在虛擬機中將變量存儲到內存和才哦哦歐諾個內存真夠去除變量這樣的底層細節。此處的變量與java變成中變量有所區別,她包括了實例字段,靜態字段和構成數據對象的元素,但是不包括局部變量與方法參數,因為後者是線程私有的,不會被共享,自然就不會存在競爭問題。為了更好地性能,java內存模型並沒有限制執行引引擎使用處理器的特定寄存器和琿春來和主內存進行交互,也沒有哦限制即時編譯器進行調整代碼執行順序這類優化。

  JMM規定所有的變量都存貯在主內存(虛擬機內存的一部分)中,每條現場還有自己的工作內存,線程的工作內存中保存了該線程使用到的主內存中的變量的副本(註意:如果一個對像如果10M,是不是會把這個10M的內存復制一份到工作內存呢?顯然是不會的,但是這個對像的引用,對像中的某個在線程中訪問到哦的字段是有可能會復制到工作能存中的,但是不會把整個對象復制一份),線程對變量的所有操作(讀取,賦值等)都需要在工作內存中進行,二不能直接讀寫主內存中的變量。不同線程之間也是無法直接訪問對方工作內存中的變量,線程間變量值的傳遞均需要通過主內存來完成。

  另外要註意,這裏所說的主內存,工作內存和java內存區域中的java堆,棧,方法區等並不是一個層次的內存劃分,這兩者沒有任何關系,如果非要勉強對應的話,主內存主要對應於java堆中的對象實例數據部分,而工作內存則對應於虛擬機棧中的部分區域。從更低層次上說,主內存就是直接對應於物理硬件的內存,而為了獲取更好額的運行速度,虛擬機可能會讓工作內存有限存儲於寄存器和高速緩存中,因為程序運行時主要訪問讀寫的是工作內存。

主內存和工作內存之間的交互

  Java內存模型定義了8種操作來完成關於主內存和工作內存之間具體的交互,這些操作都是原子的,不可分割(long double類型除外)。這8種操作如下所示:

  • 1) lock(鎖定) 作用於主內存的變量,它把一個變量標誌為一條線程獨占的狀態
  • 2) unlock(解鎖) 作用於主內存的變量,它把一個處於鎖定狀態的變量釋放出來,釋放後的變量才可以被其它線程鎖定
  • 3) read(讀取) 作用於主內存的變量,它把一個變量的值從主內存傳輸到線程的工作內存中,以便隨後的load動作使用
  • 4) load(載入) 作用於工作內存的變量,它把read操作從主內存得到的變量值放入工作內存的變量副本中
  • 5) use(使用) 作用於工作內存的變量,它把變量副本的值傳遞給執行引擎,每當虛擬機遇到一個需要使用的變量的值的字節碼指令時,將會執行這個操作。
  • 6) assign(賦值) 作用於工作內存的變量,它把一個從執行引擎接收到的值賦值給工作副本變量,每當虛擬機遇到一個給變量賦值的字節碼指令時執行這個操作
  • 7) store(存儲) 作用於工作內存的變量,將工作副本變量的值傳輸給主內存,以便隨後的write操作使用
  • 8) write(寫入) 作用於主內存的變量, 它把store操作從工作內存得到的變量的值放入主內存的變量

如果要把一個變量從主內存復制到工作內存,那就要按順序地執行read和load操作,如果要把變量從工作內存同步回主內存,那就要順序地執行store和write操作。註意,Java內存模型只要求上述兩個操作必須按順序地執行,而沒有保證必須是連續執行,也就是說read和load之間,store和write之間是可以插入其它指令的,如對內存中的變量a,b進行訪問時,一種可能出現的順序是read a, read b, load b, load a。

  除此之外,java內存模型還規定了在執行上述8中基本操作時必須滿足的規則。這裏不在詳述。

  通過這8中內存訪問操作及其相關的規定,再加上volatile的一些特殊規定,就完全可以確定哪些內存訪問操作在並發下是安全的。由於這種定義相當嚴謹但又十分的繁瑣,實踐起來很是麻煩,所以java虛擬機提供了一個等效判斷原則--先行發現原則

volatile的含義和用法

  • volatile的語義

  第一:保證了次變量對所有的線程是可見的,這裏的可見性是指當一個線程修改了這個變量的值,新值對於其他線程來說是可以立即得知的。

並發安全過程中三原則

原子性

可見性

一致性

Java內存 模型理解