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.html