Java記憶體模型與記憶體結構
Java記憶體模型
一、簡介
Java記憶體模型(JMM)主要是為了規定執行緒和記憶體之間的一些關係;根據JMM的設計,系統存在一個主記憶體(Main Memory)和工作記憶體(Work Memory),Java中所有變數都儲存在主記憶體中,對於所有執行緒都是共享的;每條執行緒都有自己的工作記憶體,工作記憶體中儲存了該執行緒已讀、寫共享變數的副本,工作記憶體是JMM的一個抽象概念,主要包括:快取,寫緩衝區,暫存器以及其他的硬體和編譯器優化;執行緒對所有變數的操作都是在工作記憶體中進行的,執行緒之間無法相互直接訪問,變數傳遞均需要通過主記憶體完成。JMM示意圖如下:
二、JMM帶來了哪些問題?
1、可見性問題
CPU中執行的執行緒從主記憶體中拷貝共享物件obj到它的CPU快取,把物件obj的count變數改為2,但這個變更對執行在右邊CPU中的執行緒不可見,因為這個更改還沒有flush到主記憶體中,要解決共享物件可見性這個問題,可以使用 volatile 或加鎖(如:synchronized),來保證可見性。
2、競爭問題
執行緒A和執行緒B共享一個物件obj,假設執行緒A從主存讀取Obj.count變數到自己的CPU快取,同時,執行緒B也讀取了Obj.count變數到自己的CPU快取,並且這兩個執行緒都對Obj.count做了加1操作;此時,Obj.count加1操作被執行了兩次,不過都在不同的CPU快取中,如果這兩個加1操作是序列執行的,那麼Obj.count變數便會在原始值上加2,最終主存中的Obj.count的值會是3;然而如果是並行操作,不管是執行緒A還是執行緒B先flush計算結果到主存,最終主記憶體中的Obj.count只會增加1次變成2;可以使用加鎖( 如:synchronized) 解決此問題,來保證一致性。
3、重排序問題
在執行程式時,為了提高效能,編譯器和處理器常常會對指令做重排序。
可以使用volatile或加鎖(如:synchronized)來保證有序性。
Java記憶體結構
先看一下結構圖:
從圖中可以看出Java記憶體結構包括五大區域:堆、方法區、虛擬機器棧、本地方法棧、程式計數器,其中堆、方法區執行緒共享,虛擬機器棧、本地方法棧、程式計數器執行緒私有。
1、堆
堆是Java虛擬機器管理的最大一塊記憶體區域,存放所有物件例項和陣列,因為堆存放的物件是執行緒共享的,所以多執行緒的時候需要同步機制;堆又劃分為:年輕代、老年代、永久代(JDK1.7)/元空間(JDK1.8),元空間與永久代的區別在於:永久代使用的是虛擬機器記憶體,元空間則採用本地記憶體。
2、虛擬機器棧
虛擬機器棧描述的是執行緒進棧出棧的過程,執行緒結束記憶體自動釋放,它用來儲存當前執行緒執行方法所需要的資料、指令、返回地址(即區域性變數和正在呼叫的方法),方法被呼叫時會在棧中開闢一塊叫棧幀的空間,方法執行在棧幀空間中。
3、本地方法棧
本地方法棧與虛擬機器棧的作用十分相似,區別是虛擬機器棧執行的是Java方法服務,而本地方法棧則為虛擬機器使用native方法服務,可能底層呼叫的c或者c++方法。
4、方法區
方法區同堆一樣,是所有執行緒共享的記憶體區域,又被稱為非堆,用於儲存已被虛擬機器載入的類資訊、常量、靜態變數等。
5、程式計數器
程式計數器是一塊很小的記憶體空間,它是執行緒私有的,可以認作是當前執行緒的行號指示器。
參考:
[1] https://www.jianshu.com/p/8a58d8335270
[2] https://www.jianshu.com/p/de097e7a813a
[3] http://tutorials.jenkov.com/java-concurrency/java-memory-model.