国产99久久精品_欧美日本韩国一区二区_激情小说综合网_欧美一级二级视频_午夜av电影_日本久久精品视频

最新文章專題視頻專題問答1問答10問答100問答1000問答2000關(guān)鍵字專題1關(guān)鍵字專題50關(guān)鍵字專題500關(guān)鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關(guān)鍵字專題關(guān)鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
當(dāng)前位置: 首頁 - 科技 - 知識百科 - 正文

阿里P8架構(gòu)師詳談Java內(nèi)存模型

來源:懂視網(wǎng) 責(zé)編:小采 時間:2020-11-27 21:50:25
文檔

阿里P8架構(gòu)師詳談Java內(nèi)存模型

阿里P8架構(gòu)師詳談Java內(nèi)存模型: 版權(quán)聲明:本文為博主原創(chuàng)文章,遵循 CC 4.0 BY-SA 版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接和本聲明。 Java 內(nèi)存模型(JMM)描述了 JVM 如何使用計(jì)算機(jī)的內(nèi)存(RAM)。JVM 是一個完整計(jì)算機(jī)的模型,因此該模型包含了內(nèi)存模型的設(shè)計(jì) —— JMM。 如果要
推薦度:
導(dǎo)讀阿里P8架構(gòu)師詳談Java內(nèi)存模型: 版權(quán)聲明:本文為博主原創(chuàng)文章,遵循 CC 4.0 BY-SA 版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接和本聲明。 Java 內(nèi)存模型(JMM)描述了 JVM 如何使用計(jì)算機(jī)的內(nèi)存(RAM)。JVM 是一個完整計(jì)算機(jī)的模型,因此該模型包含了內(nèi)存模型的設(shè)計(jì) —— JMM。 如果要
版權(quán)聲明:本文為博主原創(chuàng)文章,遵循 CC 4.0 BY-SA 版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接和本聲明。

阿里P8架構(gòu)師詳談 Java 內(nèi)存模型

?

Java 內(nèi)存模型(JMM)描述了 JVM 如何使用計(jì)算機(jī)的內(nèi)存(RAM)。JVM 是一個完整計(jì)算機(jī)的模型,因此該模型包含了內(nèi)存模型的設(shè)計(jì) —— JMM。

如果要正確地設(shè)計(jì)并發(fā)程序,了解 JMM 非常重要。JMM 描述了不同線程間如何以及何時可以看到其它線程寫入共享變量的值,以及如何在必要時同步訪問共享變量。

最初的 JMM 設(shè)計(jì)不充分,因此 JMM 在 Java 1.5 進(jìn)行了修訂。此版本的 JMM 仍在 Java 8 中使用。

Java Memory Model 內(nèi)部實(shí)現(xiàn)

JVM 內(nèi)部使用的 JMM 將內(nèi)存劃分為線程棧和堆。下圖從邏輯角度說明了 JMM:

阿里P8架構(gòu)師詳談 Java 內(nèi)存模型

?

在 JVM 中運(yùn)行的每個線程都有它自己的線程棧,線程棧包含了線程調(diào)用了哪些方法以到達(dá)當(dāng)前執(zhí)行點(diǎn)的信息,我們把它成為“調(diào)用棧(Call Stack)“。當(dāng)線程執(zhí)行其代碼時,調(diào)用棧會發(fā)生變化。

線程棧還包含了正在執(zhí)行的每個方法的所有的局部變量(調(diào)用棧上的所有方法)。一個線程只能訪問它自己的線程棧,由線程創(chuàng)建的局部變量對于創(chuàng)建它的線程以外的所有其他線程都是不可見的。即使兩個線程正在執(zhí)行完全相同的代碼,兩個線程仍將在各自的線程棧中創(chuàng)建自己的局部變量。因此,每個線程都有自己的每個局部變量的版本。

基本類型(boolean,byte,short,char,int,long,float,double)完全存儲在線程棧里,因此對其他線程是不可見的。一個線程可以將一個基本類型的變量副本傳遞給另一個線程,但它不能共享原始局部變量本身。

堆包含了 Java 應(yīng)用程序中創(chuàng)建的所有對象,不管對象是哪個線程創(chuàng)建的,這包括基本類型的包裝版本(如 Byte,Integer,Long 等)。無論對象是創(chuàng)建成局部變量,還是作為另一個對象的成員變量被創(chuàng)建,對象都存儲在堆中。

下圖說明了調(diào)用棧和局部變量存儲在線程棧中,而對象存儲在堆中。

阿里P8架構(gòu)師詳談 Java 內(nèi)存模型

?

局部變量如果是基本類型,這種情況下,變量完全存儲在線程棧上。

局部變量如果是對象的引用,這種情況下,引用(局部變量)存儲在線程棧上,但對象本身存儲在堆上。

對象中可能包含方法,而這些方法中可能包含局部變量,這種情況下,即使方法所屬的對象存儲在堆上,但這些局部變量卻是存儲在線程棧上的。

對象的成員變量與對象本身一起存儲在堆上,當(dāng)成員變量是基本類型以及是對象的引用時都是如此。

靜態(tài)類型變量與類定義一起存儲在堆上。

所有線程通過擁有對象引用去訪問堆中的對象。當(dāng)一個線程有權(quán)訪問一個對象時,它也能訪問該對象的成員變量。如果兩個線程同一時間調(diào)用同一對象的一個方法,它們都可以訪問該對象的成員變量,但每個線程都有自己局部變量的副本。

這是一個說明上述要點(diǎn)的圖表:

阿里P8架構(gòu)師詳談 Java 內(nèi)存模型

?

兩個線程各有一組局部變量,其中一個局部變量(Local Variable 2)指向堆中的共享對象(Object 3)。兩個線程各自對同一各對象擁有不同的引用,它們的引用是局部變量,因此它們存儲在各自線程的線程棧中。但是,這兩個不同引用指向堆中的同一個對象。

請注意,共享對象(Object 3)將 Object 2 和 Object 4 作為成員變量引用(如從 Object 3 到 Object 2 和 Object 4 的箭頭所示),通過對象 3 中的這些成員變量引用,兩個線程可以訪問對象 2 和 對象 4。

上圖還顯示了一個局部變量指向堆中的兩個不同對象。這種情況下,引用指向兩個不同的對象(Object 1 和 Object 5),而不是同一個對象。理論上,如果兩個線程都引用了兩個對象,那兩個線程都可以訪問對象 1 和 對象 5。但在上圖中,每個線程只引用了兩個對象中的一個。Java學(xué)習(xí)圈子?

那么,什么樣的 Java 代碼可以導(dǎo)致上面的內(nèi)存圖?好吧,代碼就如下面的代碼一樣簡單:

阿里P8架構(gòu)師詳談 Java 內(nèi)存模型

?

阿里P8架構(gòu)師詳談 Java 內(nèi)存模型

?

如果兩個線程正在執(zhí)行 run() 方法,則前面的結(jié)果就會出現(xiàn)。run() 方法會調(diào)用 methodOne(),而 methodOne() 會調(diào)用 methodTwo()。

方法 methodOne() 中聲明了一個基本類型的局部變量(localVariable1 類型 int)和一個對象引用的局部變量(localVariable2)。

每個執(zhí)行 methodOne() 的線程將在各自的線程棧上創(chuàng)建自己的 localVariable1 和 localVariable2 副本。localVariable 1 變量將完全分離,只存在于每個線程的線程棧中。一個線程無法看到另一個線程對其 localVariable 1 副本所做的更改。

執(zhí)行 methodOne() 的每個線程還將創(chuàng)建它們自己的 localVariable2 副本。然而,localVariable 2 的兩個不同副本最終都指向堆上的同一個對象。代碼將 localVariable 2 設(shè)置為指向靜態(tài)變量引用的對象。靜態(tài)變量只有一個副本,這個副本存儲在堆上。因此,localVariable 2 的兩個副本最終都指向靜態(tài)變量所指向的 MySharedObject 的同一個實(shí)例。MySharedObject 實(shí)例也存儲在堆中,它對應(yīng)于上圖中的對象 3。Java學(xué)習(xí)圈子????????

注意 MySharedObject 類也包含兩個成員變量。成員變量本身同對象一起存儲在堆中。這兩個成員變量指向另外兩個 Integer 對象,這些 Integer 對象對應(yīng)于上圖中的對象 2和對象 4。

還要注意 methodTwo() 創(chuàng)建的一個名為 localVariable 1 的本地變量。這個局部變量是一個指向 Integer 對象的對象引用。該方法將 localVariable 1 引用設(shè)置為指向一個新的 Integer 實(shí)例。localVariable 1 引用將存儲在每個執(zhí)行 methodTwo() 的線程的一個副本中。實(shí)例化的兩個 Integer 對象存儲在堆上,但是由于方法每次執(zhí)行都會創(chuàng)建一個新的 Integer 對象,因此執(zhí)行該方法的兩個線程將創(chuàng)建單獨(dú)的 Integer 實(shí)例。methodTwo() 中創(chuàng)建的 Integer 對象對應(yīng)于上圖中的對象 1和對象 5。還要注意類 MySharedObject 中的兩個成員變量,它們的類型是 long,這是一個基本類型。由于這些變量是成員變量,所以它們?nèi)匀慌c對象一起存儲在堆中。只有本地變量存儲在線程堆棧中。

硬件內(nèi)存架構(gòu)

現(xiàn)代硬件內(nèi)存架構(gòu)與 Java 內(nèi)存模型略有不同。了解硬件內(nèi)存架構(gòu)也很重要,以了解 Java 內(nèi)存模型如何與其一起工作。本節(jié)介紹了常見的硬件內(nèi)存架構(gòu),后面的部分將介紹 Java 內(nèi)存模型如何與其配合使用。

這是現(xiàn)代計(jì)算機(jī)硬件架構(gòu)的簡化圖:

阿里P8架構(gòu)師詳談 Java 內(nèi)存模型

?

現(xiàn)代計(jì)算機(jī)通常有兩個或更多的 CPU,其中一些 CPU 也可能有多個內(nèi)核。關(guān)鍵是,在具有2個或更多 CPU 的現(xiàn)代計(jì)算機(jī)上,可以同時運(yùn)行多個線程。每個 CPU 都能夠在任何給定時間運(yùn)行一個線程。這意味著如果您的 Java 應(yīng)用程序是多線程的,那么每個 CPU 可能同時(并發(fā)地)運(yùn)行 Java 應(yīng)用程序中的一個線程。

每個 CPU 包含一組寄存器,這些寄存器本質(zhì)上是在 CPU 內(nèi)存中。CPU 在這些寄存器上執(zhí)行操作的速度要比在主內(nèi)存中執(zhí)行變量的速度快得多。這是因?yàn)?CPU 訪問這些寄存器的速度要比訪問主內(nèi)存快得多。

每個 CPU 還可以有一個 CPU 緩存內(nèi)存層。事實(shí)上,大多數(shù)現(xiàn)代 CPU 都有某種大小的緩存內(nèi)存層。CPU 訪問緩存內(nèi)存的速度比主內(nèi)存快得多,但通常沒有訪問內(nèi)部寄存器的速度快。因此,CPU 高速緩存存儲器介于內(nèi)部寄存器和主存儲器的速度之間。某些 CPU 可能有多個緩存層(L1 和 L2),但要了解 Java 內(nèi)存模型如何與內(nèi)存交互,這一點(diǎn)并不重要。重要的是要知道 CPU 可以有某種緩存存儲層。

計(jì)算機(jī)還包含一個主內(nèi)存區(qū)域(RAM)。所有 CPU 都可以訪問主存,主內(nèi)存區(qū)域通常比 CPU 的緩存內(nèi)存大得多。

通常,當(dāng) CPU 需要訪問主內(nèi)存時,它會將部分主內(nèi)存讀入 CPU 緩存。它甚至可以將緩存的一部分讀入內(nèi)部寄存器,然后對其執(zhí)行操作。當(dāng) CPU 需要將結(jié)果寫回主內(nèi)存時,它會將值從內(nèi)部寄存器刷新到緩存內(nèi)存,并在某個時候?qū)⒅邓⑿禄刂鲀?nèi)存。

當(dāng)CPU需要在高速緩存中存儲其他內(nèi)容時,通常會將存儲在高速緩存中的值刷新回主內(nèi)存。CPU 緩存可以一次將數(shù)據(jù)寫入一部分內(nèi)存,并一次刷新一部分內(nèi)存。它不必每次更新時都讀取/寫入完整的緩存。通常,緩存是在稱為“緩存線(Cache Line)”的較小內(nèi)存塊中更新的。可以將一條或多條高速緩存線讀入高速緩存內(nèi)存,并將一條或多條高速緩存線再次刷新回主內(nèi)存。

JMM 和硬件內(nèi)存結(jié)構(gòu)之間的差別

如前所述,JMM 和硬件內(nèi)存結(jié)構(gòu)是不同的。硬件內(nèi)存體系結(jié)構(gòu)不區(qū)分線程棧和堆。在硬件上,線程棧和堆都位于主內(nèi)存中。線程棧和堆的一部分有時可能存在于 CPU 高速緩存和內(nèi)部 CPU 寄存器中。如下圖所示:

阿里P8架構(gòu)師詳談 Java 內(nèi)存模型

?

當(dāng)對象和變量可以存儲在計(jì)算機(jī)的不同內(nèi)存區(qū)域時,可能會出現(xiàn)某些問題。主要有兩個問題:

  • 線程更新(寫入)對共享變量的可見性
  • 讀取、檢查和寫入共享變量時的競爭條件
  • 這兩個問題將在下面幾節(jié)中進(jìn)行解釋。Java學(xué)習(xí)圈子????????

    共享對象的可見性

    如果兩個或多個線程共享一個對象,而沒有正確使用 volatile 聲明或同步,那么一個線程對共享對象的更新可能對其他線程不可見。

    假設(shè)共享對象最初存儲在主內(nèi)存中。在 CPU 1 上運(yùn)行的線程然后將共享對象讀入它的 CPU 緩存。在這里,它對共享對象進(jìn)行更改。只要沒有將 CPU 緩存刷新回主內(nèi)存,在其他 CPU 上運(yùn)行的線程就不會看到共享對象的更改版本。這樣,每個線程都可能最終擁有自己的共享對象副本,每個副本位于不同的 CPU緩 存中。

    下圖說明了大致的情況。在左 CPU 上運(yùn)行的一個線程將共享對象復(fù)制到其 CPU 緩存中,并將其 count 變量更改為2。此更改對運(yùn)行在正確 CPU 上的其他線程不可見,因?yàn)樯形磳⒏滤⑿禄刂鲀?nèi)存。

    阿里P8架構(gòu)師詳談 Java 內(nèi)存模型

    ?

    要解決這個問題,可以使用 Java 的 volatile 關(guān)鍵字。volatile 關(guān)鍵字可以確保直接從主內(nèi)存讀取給定的變量,并在更新時始終將其寫回主內(nèi)存。

    競態(tài)條件

    如果兩個或多個線程共享一個對象,且多個線程更新該共享對象中的變量,則可能出現(xiàn)競爭條件。

    假設(shè)線程 A 將共享對象的變量計(jì)數(shù)讀入其 CPU 緩存。再想象一下,線程 B 執(zhí)行相同的操作,但是進(jìn)入了不同的 CPU 緩存?,F(xiàn)在線程 A 向 count 加一,線程 B 也這樣做。現(xiàn)在 var1 已經(jīng)增加了兩次,每次在每個 CPU 緩存中增加一次。

    如果按順序執(zhí)行這些增量,變量計(jì)數(shù)將增加兩次,并將原始值 + 2 寫回主內(nèi)存。

    但是,這兩個增量是同時執(zhí)行的,沒有適當(dāng)?shù)耐?。無論哪個線程 A 和線程 B 將其更新版本的 count 寫回主內(nèi)存,更新后的值只比原始值高1,盡管有兩個增量。

    該圖說明了上述競態(tài)條件問題的發(fā)生情況:

    阿里P8架構(gòu)師詳談 Java 內(nèi)存模型

    ?

    要解決這個問題,可以使用 Java synchronized 塊。同步塊保證在任何給定時間只有一個線程可以進(jìn)入代碼的給定臨界段。Synchronized 塊還保證在 Synchronized 塊中訪問的所有變量都將從主內(nèi)存中讀入,當(dāng)線程退出 Synchronized 塊時,所有更新的變量將再次刷新回主內(nèi)存,而不管變量是否聲明為 volatile。

    粉絲福利:

    為粉絲講解福利資源:特講解免費(fèi)教程教你如何學(xué)習(xí) ,源碼、分布式、微服務(wù)、性能優(yōu)化、多線程并發(fā),從0到1,帶你領(lǐng)略底層精髓。

    如何學(xué)習(xí):

    阿里P8架構(gòu)師詳談 Java 內(nèi)存模型

    ?

    上圖中的資料都是我精心錄制視頻,感興趣的可以加入我的Java學(xué)習(xí)圈子?免費(fèi)獲取。希望能夠在你接下來即將應(yīng)對的的面試過程中能夠盡到一份綿薄之力。
    ?

    聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

    文檔

    阿里P8架構(gòu)師詳談Java內(nèi)存模型

    阿里P8架構(gòu)師詳談Java內(nèi)存模型: 版權(quán)聲明:本文為博主原創(chuàng)文章,遵循 CC 4.0 BY-SA 版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接和本聲明。 Java 內(nèi)存模型(JMM)描述了 JVM 如何使用計(jì)算機(jī)的內(nèi)存(RAM)。JVM 是一個完整計(jì)算機(jī)的模型,因此該模型包含了內(nèi)存模型的設(shè)計(jì) —— JMM。 如果要
    推薦度:
    標(biāo)簽: p8 阿里 java
    • 熱門焦點(diǎn)

    最新推薦

    猜你喜歡

    熱門推薦

    專題
    Top
    主站蜘蛛池模板: 亚洲a∨精品一区二区三区下载 | 日本a级片免费观看 | 国产码欧美日韩高清综合一区 | 欧美在线免费播放 | 国产未成女年一区二区 | 香蕉一区 | 亚洲一区二区三区久久精品 | 一级毛片免费看 | 日韩一级精品视频在线观看 | 久久99国产精品成人欧美 | 日韩欧美专区 | 国产精品色综合久久 | 久久久国产这里有的是精品 | 国产产一区二区三区久久毛片国语 | 久久国产精品视频 | 午夜视频网 | 欧美综合在线视频 | 亚洲精品乱码久久久久久中文字幕 | 欧美日韩视频在线 | 欧美日韩国产高清视频 | 天天曰夜夜曰 | 亚洲欧美精品伊人久久 | 欧美亚洲另类综合 | 成人一级免费视频 | 免费看全黄特黄毛片 | 国产欧美一区二区精品久久久 | 日韩免费一区二区三区在线 | 欧美国产日韩综合 | 亚洲精品国产成人99久久 | 热re91久久精品国产91热 | 韩国精品一区二区 | 成人午夜精品 | 欧美精品高清 | 人成精品视频三区二区一区 | 啪啪免费入口网站 | 国内一级一级毛片a免费 | 碰91精品国产91久久婷婷 | 精品久久久久久久一区二区手机版 | 永久视频在线观看 | 国产精品亚洲精品不卡 | 欧美国产在线视频 |