什么是 JMM?
在上一篇文章中,我們了解了計算機由于各個硬件的讀取速度之間的巨大差距,和充分利用CPU的性能的手段方法,及其所帶來的一系列問題:
- 為了充分壓榨CPU的性能, CPU 會對指令亂序執行或者語言的編譯器會指令重排 ,讓CPU一直工作不停歇,但同時會導致
有序性問題
。 - 為了平衡CPU的寄存器和內存的速度差異,計算機的CPU 增加了高速緩存,但同時導致了
可見性問題
- 為了平衡CPU 與 I/O 設備的速度差異,操作系統增加了進程、線程概念,以分時復用 CPU,但同時導致了
原子性問題
。
Java 是最早嘗試提供內存模型的編程語言。由于Java 語言是跨平臺的,另外各個操作系統總存在一些差異,Java在物理機器的基礎上抽象出一個內存模型(JMM)
JMM 可以看作是 Java 定義的并發編程相關的一組規范,除了抽象了線程和主內存之間的關系之外,其還規定了從 Java 源代碼到 CPU 可執行指令的這個轉化過程要遵守哪些和并發相關的原則和規范,這樣就可以屏蔽各個操作系統的差異,簡化多線程編程。
Java 運行時內存區域與硬件內存的關系
Java 內存區域和Java內存模型有何區別?
這是一個非常容易讓人混淆的問題,Java 內存區域和內存模型完全是不一樣的東西,
Java 內存區域
, 也叫內存區域
、JVM內存模型
,和 Java 虛擬機(JVM)的運行時區域相關,是指 JVM運行時將數據分區域存儲,強調對內存空間的劃分。Java內存模型
,也叫內存模型(JMM)
,是Java 定義的并發編程相關的一組規范,除了抽象了線程和主內存之間的關系之外,其還規定了從 Java 源代碼到 CPU 可執行指令的這個轉化過程要遵守哪些和并發相關的原則和規范,屏蔽各個操作系統的差異。通俗點說: JMM規范了程序中變量的訪問規則,保證了操作的原子性、可見性、有序性, 我們下文慢慢道來。
我們知道JVM 運行時內存區域是分區域的,分為棧、堆等,其實這些都是 JVM 定義的邏輯概念。但在傳統的硬件內存架構中是沒有棧和堆這種概念。
其中:
- 虛擬機棧(JVM Stacks): 線程私有,它的生命周期和線程相同 ,描述的是Java方法執行的內存模型,每個方法在執行的同時都會創建一個線幀(Stack Frame)用于存儲局部變量表、操作數棧、動態鏈接、方法出口等信息,每個方法從調用直至執行完成的過程,都對應著一個線幀在虛擬機棧中入棧到出棧的過程
- 本地方法棧(Native Method Stack): 線程私有 ,本地方法棧與虛擬機棧的作用是一樣的,只不過虛擬機棧是服務Java方法的,而本地方法棧是為虛擬機調用Native方法服務的。在Java虛擬機規范中對于本地方法棧沒有特殊的要求,虛擬機可以自由的實現它,因此 在Sun HotSpot虛擬機直接把本地方法棧和虛擬機棧合二為一了 。線程開始調用本地方法時,會進入 不再受 JVM 約束的世界。本地方法可以通過
JNI(Java Native Interface)
來訪問虛擬機運行時的數據區,甚至可以調用寄存器,具有和 JVM 相同的能力和權限。JNI 類本地方法最著名的應該是System.currentTimeMillis()
- 堆(Heap)
虛擬機堆是Java虛擬機中內存最大的一塊,是被所有線程共享的,在虛擬機啟動時候創建,Java堆唯一的目的就是存放對象實例,幾乎所有的對象實例都在這里分配內存,隨著JIT編譯器的發展和逃逸分析技術的逐漸成熟,棧上分配、標量替換優化的技術將會導致一些微妙的變化,所有的對象都分配在堆上漸漸變得不那么“絕對”了。
Java中棧和堆
既存在于計算機的高速緩存中,又存在于主存
中,所以兩者并沒有很直接的關系。
Java 線程與主內存的關系
Java 內存模型(JMM) 抽象了線程和主內存之間的關系,就比如說線程之間的共享變量必須存儲在主內存中。在 JDK1.2 之前,Java 的內存模型實現總是從 主存 (即共享內存)讀取變量,是不需要進行特別的注意的。而在當前的 Java 內存模型下,線程可以把變量保存 本地內存 (比如機器的寄存器)中,而不是直接在主存中進行讀寫。這就可能造成一個線程在主存中修改了一個變量的值,而另外一個線程還繼續使用它在寄存器中的變量值的拷貝,造成數據的不一致。
什么是主內存?什么是本地內存?
- 主內存 :所有線程創建的實例對象都存放在主內存中,不管該實例對象是成員變量還是方法中的本地變量(也稱局部變量)
- 本地內存 :每個線程都有一個私有的本地內存來存儲共享變量的副本,并且,每個線程只能訪問自己的本地內存,無法訪問其他線程的本地內存。本地內存是
JMM
抽象出來的一個概念,存儲了主內存中的共享變量副本。
Java 內存模型其實是一種規范,定義了很多東西:
- 所有的變量都存儲在主內存(Main Memory)中。
- 每個線程都有一個私有的本地內存 (Local Memory),本地內存中存儲了該線程以讀/寫共享變量的拷貝副本。
- 線程對變量的所有操作都必須在本地內存中進行,而不能直接讀寫主內存。
- 不同的線程之間無法直接訪問對方本地內存中的變量。
這里所講的主內存、工作內存與 Java 內存區域中的 Java 堆、棧、方法區等并不是同一個層次的內存劃分,這兩者基本上是沒有關系的,如果兩者一定要勉強對應起來,那從變量、主內存、工作內存的定義來看,主內存主要對應于Java堆中的對象實例數據部分,而工作內存則對應于虛擬機棧中的部分區域。
-
JAVA
+關注
關注
19文章
2960瀏覽量
104563 -
編譯器
+關注
關注
1文章
1618瀏覽量
49057 -
JVM
+關注
關注
0文章
157瀏覽量
12210
發布評論請先 登錄
相關推薦
評論