JVM內(nèi)存區(qū)域與多線程

Java并發(fā)的機(jī)制的背后是Java虛擬機(jī)(JVM)的工作機(jī)制,本文從幾個關(guān)于并發(fā)和多線程的疑問開始,引出Java內(nèi)存區(qū)域的介紹被廓,希望能幫助大家更好的理解Java并發(fā)機(jī)制。

1. 線程創(chuàng)建和切換的代價(jià)——JVM的內(nèi)存區(qū)域

在《從任務(wù)到線程:Java結(jié)構(gòu)化并發(fā)應(yīng)用程序》和《嘗試Java加鎖新思路:原子變量和非阻塞同步算法》中椿每,曾經(jīng)分別介紹過伊者,創(chuàng)建線程和線程間切換對于性能和資源的消耗是不容忽視的,無限制地創(chuàng)建線程會消耗過多的內(nèi)存資源并不可取间护,過多的線程間上下文切換也會降低多線程并發(fā)的性能亦渗。但是線程創(chuàng)建和切換的代價(jià)到底是怎么產(chǎn)生的呢?這就不得不提到Java的運(yùn)行時(shí)數(shù)據(jù)區(qū)了汁尺。

1.1 JVM運(yùn)行時(shí)數(shù)據(jù)區(qū)

JVM運(yùn)行時(shí)數(shù)據(jù)區(qū)

根據(jù)《Java虛擬機(jī)規(guī)范》 JVM 將所管理的內(nèi)存區(qū)域劃分為 Method Area方法區(qū))法精,Heap),Program Counter Register程序計(jì)數(shù)器), VM Stack(虛擬機(jī)棧)痴突,Native Method Stack本地方法棧)搂蜓,其中Method Area和Heap是線程共享的,VM Stack辽装,Native Method Stack 和Program Counter Register是線程隔離的帮碰。

如果讀者對于JVM運(yùn)行時(shí)數(shù)據(jù)區(qū)不是很了解,由于篇幅有限拾积,請參看JVM初探 -JVM內(nèi)存模型淺析Java虛擬機(jī)結(jié)構(gòu)與機(jī)制殉挽,這里不再展開說明丰涉,只提供一份思維導(dǎo)圖,幫助大家梳理內(nèi)容:

Java內(nèi)存區(qū)域.png

概括地說來斯碌,JVM初始運(yùn)行的時(shí)候都會分配好Method Area(方法區(qū))和Heap(堆)一死,而JVM 每遇到一個線程,就為其分配一個Program Counter Register(程序計(jì)數(shù)器), VM Stack(虛擬機(jī)棧)和Native Method Stack (本地方法棧)傻唾,當(dāng)線程終止時(shí)投慈,三者(虛擬機(jī)棧,本地方法棧和程序計(jì)數(shù)器)所占用的內(nèi)存空間也會被釋放掉冠骄。

1.2 線程創(chuàng)建的內(nèi)存代價(jià)

每當(dāng)有線程被創(chuàng)建的時(shí)候伪煤,JVM就需要為其在內(nèi)存中分配虛擬機(jī)棧和本地方法棧來記錄調(diào)用方法的內(nèi)容,分配程序計(jì)數(shù)器記錄指令執(zhí)行的位置猴抹,這樣的內(nèi)存消耗就是創(chuàng)建線程的內(nèi)存代價(jià)带族。

內(nèi)存作為有限的資源锁荔,如果JVM創(chuàng)建了過多的線程蟀给,必然會導(dǎo)致資源的耗盡。因此阳堕,使用Executor架構(gòu)復(fù)用線程可以節(jié)省內(nèi)存資源跋理,是十分必要的。

1.3 線程切換的性能代價(jià)

JVM的并發(fā)是通過線程切換并分配時(shí)間片執(zhí)行來實(shí)現(xiàn)的. 在任何一個時(shí)刻, 一個處理器內(nèi)核只會執(zhí)行一條線程中的指令恬总。因此, 為了線程切換后能恢復(fù)到正確的執(zhí)行位置, JVM需要先保存被掛起線程的上下文環(huán)境:將線程執(zhí)行位置保存到程序計(jì)數(shù)器中前普,將調(diào)用方法的信息保存在棧中;同時(shí)將待執(zhí)行線程的程序計(jì)數(shù)器和棧中的信息寫入到處理器中壹堰,完成線程的上下文切換拭卿。維護(hù)線程隔離數(shù)據(jù)區(qū)中的內(nèi)容在處理器中的導(dǎo)入導(dǎo)出,就是線程切換的性能代價(jià)贱纠。

控制線程上下文切換次數(shù)的方法有很多:

  1. 使用基于CAS的非擁塞算法峻厚,詳見嘗試Java加鎖新思路:原子變量和非阻塞同步算法
  2. 無鎖并發(fā)編程谆焊,盡量使用線程封閉(ThreadLocal)或者不變量惠桃,而不是用鎖,詳見對象共享:Java并發(fā)環(huán)境中的煩心事辖试;
  3. 使用線程池+等待隊(duì)列的方式辜王,控制線程數(shù)目,詳見從任務(wù)到線程:Java結(jié)構(gòu)化并發(fā)應(yīng)用程序罐孝;

2. 對象訪問的定位 VS 內(nèi)存可見性

根據(jù)JVM運(yùn)行時(shí)的內(nèi)存模式呐馆,在一個方法中使用某個變量,JVM要現(xiàn)在棧中找到該變量的引用莲兢,然后通過引用找到該對象在堆中保存到實(shí)例數(shù)據(jù)汹来,如下圖所示:


對象訪問的定位

但是這個過程為什么會出現(xiàn)多線程間的內(nèi)存可見性的問題呢辫继?

這個過程從單個線程的角度來說,是沒有問題的俗慈,線程可以找到自己需要的變量姑宽,但是得到的變量不一定是最新的,這是由于主存和緩存內(nèi)容不一致造成的闺阱。

JVM不光有內(nèi)存區(qū)域的劃分炮车,還有內(nèi)存模式(JMM)來控制Java線程見的通信,其決定了一個線程的共享變量的寫入合適對另一個線程可見酣溃。在Java中通過多線程機(jī)制使得多個任務(wù)同時(shí)執(zhí)行處理瘦穆,所有的線程共享JVM內(nèi)存區(qū)域主存(main memory),而每個線程又單獨(dú)的有自己的工作內(nèi)存赊豌,當(dāng)線程與內(nèi)存區(qū)域進(jìn)行交互時(shí)扛或,數(shù)據(jù)從主存拷貝到工作內(nèi)存,進(jìn)而交由線程處理(操作碼+操作數(shù))碘饼,數(shù)據(jù)寫入時(shí)熙兔,先被寫入到工作緩存中,JMM選擇合適的時(shí)機(jī)將其同步到主存中艾恼,以此提高訪問效率住涉。


JMM中的主存和緩存

工作緩存其實(shí)是一個抽象的概念,Java的內(nèi)存分區(qū)中并沒有專門的一塊區(qū)域叫線程的工作緩存钠绍,其實(shí)際上是緩存舆声、對讀寫緩沖區(qū)、寄存器以及其他硬件和編譯器優(yōu)化的統(tǒng)稱柳爽。

因此虛擬棧到Java堆中尋找實(shí)例數(shù)據(jù)和JMM并不矛盾媳握,二者是JVM不通角度的闡述。

3. 對象的創(chuàng)建——靜態(tài)區(qū)域加載的多線程安全性

為了保證多線程安全性磷脯,一些必要的操作都需要加鎖來保證其原子性和可見性蛾找,但是類中靜態(tài)區(qū)的代碼是不需要加鎖就能保證多線程安全性。這是因?yàn)槭裁茨兀?/p>

答案在于JVM類加載過程的保護(hù)機(jī)制争拐。和普通類的實(shí)例被分配在Java堆上不同腋粥,類的靜態(tài)屬性都保存在方法區(qū),其創(chuàng)建收到類加載過程的影響架曹。


方法區(qū)隘冲,又名非堆

類的加載過程大題分為:加載(Loading),連接(Linking)绑雄,初始化(Initialization)展辞,使用(Using)和卸載(UnLoading)五個步驟。這里和靜態(tài)屬性有關(guān)的主要是連接和初始化:在連接步驟的準(zhǔn)備階段万牺,靜態(tài)屬性會分配內(nèi)存罗珍;在初始化步驟洽腺,JVM會生成一個特別的方法——<clinit>方法來專門執(zhí)行靜態(tài)代碼塊和靜態(tài)變量初始化。

<clinit>方法執(zhí)行的過程中覆旱,JVM會對類加鎖蘸朋,保證在多線程環(huán)境下,只有一個線程能成功執(zhí)行<clinit>方法扣唱,其他線程都將被擁塞藕坯,并且<clinit>方法只能被執(zhí)行一次,被擁塞的線程被喚醒之后也不會再去執(zhí)行<clinit>方法噪沙。如果類有繼承關(guān)系炼彪,JVM還會保證父類的<clinit>方法將先于子類的<clinit>方法執(zhí)行。

由此可見正歼,靜態(tài)代碼塊的多線程安全性是由JVM為其加鎖實(shí)現(xiàn)的辐马,這也是延遲初始化占位類模式的安全性基礎(chǔ),詳見《從Java內(nèi)存模型角度理解安全初始化》局义。

參考文獻(xiàn)

  1. 深入理解JVM之JVM內(nèi)存區(qū)域與內(nèi)存分配
  2. 深入理解JVM—JVM內(nèi)存模型
  3. JVM 內(nèi)存初學(xué) (堆(heap)喜爷、棧(stack)和方法區(qū)(method) )
  4. Java常量池詳解之一道比較蛋疼的面試題
  5. JVM中鎖優(yōu)化簡介
  6. JVM初探 -JVM內(nèi)存模型
  7. 淺析Java虛擬機(jī)結(jié)構(gòu)與機(jī)制
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市旭咽,隨后出現(xiàn)的幾起案子贞奋,更是在濱河造成了極大的恐慌,老刑警劉巖穷绵,帶你破解...
    沈念sama閱讀 218,607評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異特愿,居然都是意外死亡仲墨,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評論 3 395
  • 文/潘曉璐 我一進(jìn)店門揍障,熙熙樓的掌柜王于貴愁眉苦臉地迎上來目养,“玉大人,你說我怎么就攤上這事毒嫡“┮希” “怎么了?”我有些...
    開封第一講書人閱讀 164,960評論 0 355
  • 文/不壞的土叔 我叫張陵兜畸,是天一觀的道長努释。 經(jīng)常有香客問我,道長咬摇,這世上最難降的妖魔是什么伐蒂? 我笑而不...
    開封第一講書人閱讀 58,750評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮肛鹏,結(jié)果婚禮上逸邦,老公的妹妹穿的比我還像新娘恩沛。我一直安慰自己,他們只是感情好缕减,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評論 6 392
  • 文/花漫 我一把揭開白布雷客。 她就那樣靜靜地躺著,像睡著了一般桥狡。 火紅的嫁衣襯著肌膚如雪佛纫。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,604評論 1 305
  • 那天总放,我揣著相機(jī)與錄音呈宇,去河邊找鬼。 笑死局雄,一個胖子當(dāng)著我的面吹牛甥啄,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播炬搭,決...
    沈念sama閱讀 40,347評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蜈漓,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了宫盔?” 一聲冷哼從身側(cè)響起融虽,我...
    開封第一講書人閱讀 39,253評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎灼芭,沒想到半個月后有额,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,702評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡彼绷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評論 3 336
  • 正文 我和宋清朗相戀三年巍佑,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片寄悯。...
    茶點(diǎn)故事閱讀 40,015評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡萤衰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出猜旬,到底是詐尸還是另有隱情脆栋,我是刑警寧澤,帶...
    沈念sama閱讀 35,734評論 5 346
  • 正文 年R本政府宣布洒擦,位于F島的核電站椿争,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏秘遏。R本人自食惡果不足惜丘薛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望邦危。 院中可真熱鬧洋侨,春花似錦舍扰、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至裁僧,卻和暖如春个束,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背聊疲。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評論 1 270
  • 我被黑心中介騙來泰國打工茬底, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人获洲。 一個月前我還...
    沈念sama閱讀 48,216評論 3 371
  • 正文 我出身青樓阱表,卻偏偏與公主長得像,于是被迫代替她去往敵國和親贡珊。 傳聞我的和親對象是個殘疾皇子最爬,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 從三月份找實(shí)習(xí)到現(xiàn)在,面了一些公司门岔,掛了不少爱致,但最終還是拿到小米、百度寒随、阿里糠悯、京東、新浪牢裳、CVTE逢防、樂視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,253評論 11 349
  • 這篇文章是我之前翻閱了不少的書籍以及從網(wǎng)絡(luò)上收集的一些資料的整理,因此不免有一些不準(zhǔn)確的地方蒲讯,同時(shí)不同JDK版本的...
    高廣超閱讀 15,604評論 3 83
  • 下面是我自己收集整理的Java線程相關(guān)的面試題,可以用它來好好準(zhǔn)備面試灰署。 參考文檔:-《Java核心技術(shù) 卷一》-...
    阿呆變Geek閱讀 14,836評論 14 507
  • JVM內(nèi)存模型Java虛擬機(jī)(Java Virtual Machine=JVM)的內(nèi)存空間分為五個部分判帮,分別是: ...
    光劍書架上的書閱讀 2,510評論 2 26
  • 在一個陽光明媚的上午,我到外公家去玩溉箕。 離外公家的不遠(yuǎn)處是一些低低矮矮的山丘晦墙,山丘上是一片片的橘子林,這里是我和哥...
    翱藍(lán)閱讀 987評論 2 1