日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

作者 l Hollis 來源 l Hollis(ID:hollischuang)

JVM內(nèi)存結(jié)構(gòu),是很重要的知識(shí),相信每一個(gè)靜心準(zhǔn)備過面試的程序員都可以清楚的把堆、棧、方法區(qū)等介紹的比較清楚。

 

上圖,是一張?jiān)谧髡吒鶕?jù)《JAVA虛擬機(jī)規(guī)范(Java SE 8)》中描述的JVM運(yùn)行時(shí)內(nèi)存區(qū)域結(jié)構(gòu)畫的。

很多人都知道Java對(duì)象是在堆內(nèi)存中分配空間的(JIT優(yōu)化除外),也知道內(nèi)存分配過程中是線程安全的,那么虛擬機(jī)到底是如何保證線程安全的呢?本文就來簡(jiǎn)單介紹一下。

Java對(duì)象的內(nèi)存分配

我們知道,Java是一門面向?qū)ο蟮恼Z言,我們?cè)贘ava中使用的對(duì)象都需要被創(chuàng)建出來,在Java中,創(chuàng)建一個(gè)對(duì)象的方法有很多種,如使用new、使用反射、使用Clone方法等,但是無論如何,對(duì)象在創(chuàng)建過程中,都需要進(jìn)行內(nèi)存分配。

拿最常見的new關(guān)鍵字舉例,當(dāng)我們使用new創(chuàng)建對(duì)象后代碼開始運(yùn)行后,虛擬機(jī)執(zhí)行到這條new指令的時(shí)候,會(huì)先檢查要new的對(duì)象對(duì)應(yīng)的類是否已被加載,如果沒有被加載則先進(jìn)行類加載。

在類加載檢查通過之后,就需要給對(duì)象進(jìn)行內(nèi)存分配了,分配的內(nèi)存主要用來存放對(duì)象的實(shí)例變量。

在進(jìn)行內(nèi)存分配時(shí),需要根據(jù)對(duì)象中的實(shí)例變量情況等信息確定需要分配的空間大小,然后從Java堆中劃分出這樣一塊區(qū)域(假設(shè)沒有JIT優(yōu)化)。

根據(jù)JVM使用的垃圾回收器的類型,因其回收算法不同,會(huì)導(dǎo)致堆中內(nèi)存分配情況不同。如標(biāo)記-清楚算法回收后的內(nèi)存中會(huì)有大量不連續(xù)的內(nèi)存碎片,在給新的對(duì)象分配的時(shí)候,就需要通過"空閑列表"來確定一塊空閑區(qū)域。(這部分不是本文重點(diǎn),讀者可以自行學(xué)習(xí)一下。)

無論那種方式,最終都需要確定出一塊內(nèi)存區(qū)域,用于給新建對(duì)象分配內(nèi)存。我們知道,對(duì)象的內(nèi)存分配過程中,主要是對(duì)象的引用指向這個(gè)內(nèi)存區(qū)域,然后進(jìn)行初始化操作。

那么問題就來了:

在并發(fā)場(chǎng)景中,如何內(nèi)存分配過程的線程安全性?如果兩個(gè)線程先后把對(duì)象引用指向了同一個(gè)內(nèi)存區(qū)域,怎么辦。

TLAB

一般有兩種解決方案:

  • 1、對(duì)分配內(nèi)存空間的動(dòng)作做同步處理,采用CAS機(jī)制,配合失敗重試的方式保證更新操作的線程安全性。
  • 2、每個(gè)線程在Java堆中預(yù)先分配一小塊內(nèi)存,然后再給對(duì)象分配內(nèi)存的時(shí)候,直接在自己這塊"私有"內(nèi)存中分配,當(dāng)這部分區(qū)域用完之后,再分配新的"私有"內(nèi)存。

方案1在每次分配時(shí)都需要進(jìn)行同步控制,這種是比較低效的。

方案2是HotSpot虛擬機(jī)中采用的,這種方案被稱之為TLAB分配,即Thread Local Allocation Buffer。這部分Buffer是從堆中劃分出來的,但是是本地線程獨(dú)享的。

這里值得注意的是,我們說TLAB時(shí)線程獨(dú)享的,但是只是在“分配”這個(gè)動(dòng)作上是線程獨(dú)占的,至于在讀取、垃圾回收等動(dòng)作上都是線程共享的。而且在使用上也沒有什么區(qū)別。

另外,TLAB僅作用于新生代的Eden Space,對(duì)象被創(chuàng)建的時(shí)候首先放到這個(gè)區(qū)域,但是新生代分配不了內(nèi)存的大對(duì)象會(huì)直接進(jìn)入老年代。因此在編寫Java程序時(shí),通常多個(gè)小的對(duì)象比大的對(duì)象分配起來更加高效。

所以,雖然對(duì)象剛開始可能通過TLAB分配內(nèi)存,存放在Eden區(qū),但是還是會(huì)被垃圾回收或者被移到Survivor Space、Old Gen等。

不知道大家有沒有想過,我們使用了TLAB之后,在TLAB上給對(duì)象分配內(nèi)存時(shí)線程獨(dú)享的了,這就沒有沖突了,但是,TLAB這塊內(nèi)存自身從堆中劃分出來的過程也可能存在內(nèi)存安全問題啊。

所以,在對(duì)于TLAB的分配過程,還是需要進(jìn)行同步控制的。但是這種開銷相比于每次為單個(gè)對(duì)象劃分內(nèi)存時(shí)候?qū)M(jìn)行同步控制的要低的多。

虛擬機(jī)是否使用TLAB是可以選擇的,可以通過設(shè)置-XX:+/-UseTLAB參數(shù)來指定。

總結(jié)

為了保證Java對(duì)象的內(nèi)存分配的安全性,同時(shí)提升效率,每個(gè)線程在Java堆中可以預(yù)先分配一小塊內(nèi)存,這部分內(nèi)存稱之為TLAB(Thread Local Allocation Buffer),這塊內(nèi)存的分配時(shí)線程獨(dú)占的,讀取、使用、回收是線程共享的。

可以通過設(shè)置-XX:+/-UseTLAB參數(shù)來指定是否開啟TLAB分配。

參考資料

《深入理解Java虛擬機(jī)》

https://www.cnblogs.com/straybirds/p/8529924.html https://www.zhihu.com/question/56538259

分享到:
標(biāo)簽:內(nèi)存 Java
用戶無頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定