小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

JVM內(nèi)存模型總結(jié)

 印度阿三17 2019-06-27

文章目錄

整體架構(gòu)

JVM = 類加載器(classloader)   執(zhí)行引擎
(executionengine)   運行時數(shù)據(jù)區(qū)域(runtime dataarea)
java每個線程都有一個虛擬機棧

類加載器 ClassLoader

1.加載過程 7 個步驟 加載–>驗證–>準備–>解析–>初始化–>使用–>卸載
在這里插入圖片描述
2.ClassLoader–>BaseDexClassLoader–>PathClassLoder ,DexClassLoader

3.雙親委托模式 先走父類的加載器加載類,若果沒有找打父類才會輪到自己,好處 避免重復(fù)加載 安全

運行時數(shù)據(jù)區(qū)

  • 運行時數(shù)據(jù)區(qū) 包涵 5 大部分
    方法區(qū)、堆、棧、本地方法棧、程序計數(shù)器

1.程序計數(shù)器 :

我們知道對于一個處理器(如果是多核cpu那就是一核),在一個
確定的時刻都只會執(zhí)行一條線程中的指令,一條線程中有多個指
令,為了線程切換可以恢復(fù)到正確執(zhí)行位置,每個線程都需有獨
立的一個程序計數(shù)器,不同線程之間的程序計數(shù)器互不影響,獨立存儲。    

2.本地方法棧 : 簡單的理解為 C 代碼的執(zhí)行去

3.堆 : 簡單的說就是對象的存儲區(qū),它是被所有線程共享的一塊區(qū)域

堆是java虛擬機管理內(nèi)存最大的一塊內(nèi)存區(qū)域,因為堆存放的
對象是線程共享的,所以多線程的時候也需要同步機制。
堆 回收算法使用的復(fù)制算法 效率高 沒有碎片 利用率低
分為三個區(qū) eden  from  to (survivor) 按照 8:1:1因為大多數(shù)的對象都是朝生夕死的。

4.棧 :

棧描述的是Java方法執(zhí)行的內(nèi)存模型。
每個方法被執(zhí)行的時候都會創(chuàng)建一個棧幀用于存儲局部變量表,
操作棧,動態(tài)鏈接,方法出口等信息。每一個方法被調(diào)用的過程
就對應(yīng)一個棧幀在虛擬機棧中從入棧到出棧的過程。

5.方法區(qū)

方法區(qū)同堆一樣,是所有線程共享的內(nèi)存區(qū)域,為了區(qū)分堆,又
被稱為非堆。用于存儲已被虛擬機加載的類信息、常量、靜態(tài)變
量,如static修飾的變量加載類的時候就被加載到方法區(qū)中。

6.GC

  1. 堆的回收為了高效的回收,jvm將堆分為三個區(qū)域
    1.新生代(Young Generation)NewSize和MaxNewSize分別可以控制年輕代的初始大小和最大的大小
    2.老年代(Old Generation)
    3.永久代(Permanent Generation)【1.8以后采用元空間,就不在堆中了】

  2. 對象是否存活

    1. 引用計數(shù)算法
      早期判斷對象是否存活大多都是以這種算法,這種算法判斷很簡單,簡單來說就是給對象添加一個引用計數(shù)器,每當(dāng)對象被引用一次就加1,引用失效時就減1。當(dāng)為0的時候就判斷對象不會再被引用。
      優(yōu)點:實現(xiàn)簡單效率高,被廣泛使用與如python何游戲腳本語言上。
      缺點:難以解決循環(huán)引用的問題,就是假如兩個對象互相引用已經(jīng)不會再被其它其它引用,導(dǎo)致一直不會為0就無法進行回收。

    2. 可達性分析算法
      目前主流的商用語言[如java、c#]采用的是可達性分析算法判斷對象是否存活。這個算法有效解決了循環(huán)利用的弊端。
      它的基本思路是通過一個稱為“GC Roots”的對象為起始點,搜
      索所經(jīng)過的路徑稱為引用鏈,當(dāng)一個對象到GC Roots沒有任何引用跟它連接則證明對象是不可用的。

  3. 回收算法

1.標(biāo)記/清除算法【最基礎(chǔ)】 (老年代 標(biāo)記清楚 、整理 新生代 是復(fù)制算法)

標(biāo)記 也是紅灰白  對灰色進行清除 標(biāo)記整理是紅色進行整
理 ,清楚會有很多碎片,效率高

2.復(fù)制算法

復(fù)制內(nèi)存區(qū)域,標(biāo)記 紅藍灰白色   紅色不可回收  灰色可回
收  白色沒有分配 藍色 預(yù)留 , 效率高,內(nèi)存復(fù)制沒有碎
片,缺點  利用率只有一半() 

3.標(biāo)記/整理算法

jvm采用`分代收集算法`對不同區(qū)域采用不同的回收算法。
其中新生代使用的是復(fù)制算法,老年代使用的是標(biāo)記清除、標(biāo)記整理算法?!?/code>

jvm 三大特性 可見性 原子性 有序性

volatile 具有可見性和禁止命令重排序,不保證原子性。所以能夠達到一次修改其他線程可見

原子性

即一個操作或者多個操作 要么全部執(zhí)行并且執(zhí)行的過程不會被任何因素打斷,要么就都不執(zhí)行。

  • 例如 i=0 只有一步操作 把0賦給變量i i 則不是,先取出i 再執(zhí)行i 1 最后把結(jié)果賦給i 進行了三部,不符合原子性

  • 在單線程環(huán)境下我們可以認為以上都是原子性操作,但是在多線程環(huán)境下則不同,Java只保證了基本數(shù)據(jù)類型的變量和賦值操作才是原子性的(注:在32位的JDK環(huán)境下,對64位數(shù)據(jù)的讀取不是原子性操作*,如long、double)。要想在多線程環(huán)境下保證原子性,則可以通過鎖、synchronized來確保。

可見性

可見性是指當(dāng)多個線程訪問同一個變量時,一個線程修改了這個變量的值,其他線程能夠立即看得到修改的值。

  • 在多線程環(huán)境下,一個線程對共享變量的操作對其他線程是不可見的。

有序性

有序性:即程序執(zhí)行的順序按照代碼的先后順序執(zhí)行。

  • 在Java內(nèi)存模型中,為了效率是允許編譯器和處理器對指令進行重排序,當(dāng)然重排序它不會影響單線程的運行結(jié)果,但是對多線程會有影響。

volatile實現(xiàn)原理

volatile可以保證線程可見性且提供了一定的有序性,但是無法保證原子性,即 volatile 保證了 可見性和有序性 沒有原子性 。在JVM底層volatile是采用“內(nèi)存屏障”來實現(xiàn)的。

  • 問題的提出 計算機在運行程序時,每條指令都是在CPU中執(zhí)行的,在執(zhí)行過程中勢必會涉及到數(shù)據(jù)的讀寫。我們知道程序運行的數(shù)據(jù)是存儲在主存中,這時就會有一個問題,讀寫主存中的數(shù)據(jù)沒有CPU中執(zhí)行指令的速度快,如果任何的交互都需要與主存打交道則會大大影響效率,所以就有了CPU高速緩存。CPU高速緩存為某個CPU獨有,只與在該CPU運行的線程有關(guān)。

有了CPU高速緩存雖然解決了效率問題,但是它會帶來一個新的問題:數(shù)據(jù)一致性。在程序運行中,會將運行所需要的數(shù)據(jù)復(fù)制一份到CPU高速緩存中,在進行運算時CPU不再也主存打交道,而是直接從高速緩存中讀寫數(shù)據(jù),只有當(dāng)運行結(jié)束后才會將數(shù)據(jù)刷新到主存中。舉一個簡單的例子:

    i  i  
    當(dāng)線程運行這段代碼時,
    首先會從主存中讀取i( i = 1),
    然后復(fù)制一份到CPU高速緩存中,
    然后CPU執(zhí)行   1 (2)的操作,
    然后將數(shù)據(jù)(2)寫入到告訴緩存中,
    最后刷新到主存中。
    其實這樣做在單線程中是沒有問題的,有問題的是在多線程中。如下:
    
    假如有兩個線程A、B都執(zhí)行這個操作(i  ),按照我們正常的邏輯思維主存中的i值應(yīng)該=3,但事實是這樣么?分析如下:
    
    兩個線程從主存中讀取i的值(1)到各自的高速緩存中,然后線程A執(zhí)行 1操作并將結(jié)果寫入高速緩存中,最后寫入主存中,此時主存i==2,線程B做同樣的操作,主存中的i仍然=2。所以最終結(jié)果為2并不是3。這種現(xiàn)象就是緩存一致性問題。
  • 解決緩存一致性方案有兩種:

  1. 通過在總線加LOCK#鎖的方式
    通過緩存一致性協(xié)議
    但是方案1存在一個問題,它是采用一種獨占的方式來實現(xiàn)的,即總線加LOCK#鎖的話,只能有一個CPU能夠運行,其他CPU都得阻塞,效率較為低下。

  2. 第二種方案,緩存一致性協(xié)議(MESI協(xié)議)它確保每個緩存中使用的共享變量的副本是一致的。其核心思想如下:當(dāng)某個CPU在寫數(shù)據(jù)時,如果發(fā)現(xiàn)操作的變量是共享變量,則會通知其他CPU告知該變量的緩存行是無效的,因此其他CPU在讀取該變量時,發(fā)現(xiàn)其無效會重新從主存中加載數(shù)據(jù)。

一個int變量,用volatile修飾,多線程去操作 ,線程安全嗎?

    不安全。volatile只能保證可見性,并不能保證原子性。
    i  實際上會被分成多步完成:
    1)獲取i的值;
    2)執(zhí)行i 1;
    3)將結(jié)果賦值給i。
    volatile只能保證這3步不被重排序,多線程情況下,可能兩個線程同時獲取i,執(zhí)行i 1,然后都賦值結(jié)果2,實際上應(yīng)該進行兩次 1操作。

那如何才能保證i 線程安全?

可以使用java.util.concurrent.atomic包下的原子類,如AtomicInteger。
其實現(xiàn)原理是采用CAS自旋操作更新值。CAS即compare and swap的縮寫,中文翻譯成比較并交換。CAS有3個操作數(shù),內(nèi)存值V,舊的預(yù)期值A(chǔ),要修改的新值B。當(dāng)且僅當(dāng)預(yù)期值A(chǔ)和內(nèi)存值V相同時,將內(nèi)存值V修改為B,否則什么都不做。自旋就是不斷嘗試CAS操作直到成功為止。

CAS實現(xiàn)原子操作會出現(xiàn)什么問題?

ABA問題。因為CAS需要在操作之的時候,檢查值有沒有發(fā)生變化,如果沒有發(fā)生變化則更新,但是如果一個值原來是A,變成,有變成A,那么使用CAS進行檢查時會發(fā)現(xiàn)它的值沒有發(fā)生變化,但實際上發(fā)生了變化。ABA問題可以通過添加版本號來解決。Java 1.5開始,JDK的Atomic包里提供了一個類AtomicStampedReference來解決ABA問題。
循環(huán)時間長開銷大。pause指令優(yōu)化。
只能保證一個共享變量的原子操作??梢院喜⒊梢粋€對象進行CAS操作。

  • 總結(jié) :volatile 可見性的原理是通過內(nèi)存屏障來實現(xiàn)的。對這個標(biāo)志符修飾的變量不再從高速緩沖中讀取數(shù)據(jù),讓所有訪問該變量的線程都直接從主存中讀取數(shù)據(jù),以此來保證數(shù)據(jù)的可見性。

參考 https://www.jianshu.com/p/76959115d486 //這個文章比較好,講解了volatile 同時還講解了jvm內(nèi)存模型
https://www.cnblogs.com/chenssy/p/6379280.html

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多