本篇內(nèi)容主要講解“Java對象的內(nèi)存分配是怎么保證線程安全的”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學(xué)習(xí)“Java對象的內(nèi)存分配是怎么保證線程安全的”吧!
10年積累的成都網(wǎng)站設(shè)計、做網(wǎng)站經(jīng)驗,可以快速應(yīng)對客戶對網(wǎng)站的新想法和需求。提供各種問題對應(yīng)的解決方案。讓選擇我們的客戶得到更好、更有力的網(wǎng)絡(luò)服務(wù)。我雖然不認(rèn)識你,你也不認(rèn)識我。但先網(wǎng)站制作后付款的網(wǎng)站建設(shè)流程,更有山南免費網(wǎng)站建設(shè)讓你可以放心的選擇與我們合作。
JVM內(nèi)存結(jié)構(gòu),是很重要的知識,相信每一個靜心準(zhǔn)備過面試的程序員都可以清楚的把堆、棧、方法區(qū)等介紹的比較清楚。
上圖,是一張在作者根據(jù)《Java虛擬機規(guī)范(Java SE 8)》中描述的JVM運行時內(nèi)存區(qū)域結(jié)構(gòu)畫的。
很多人都知道Java對象是在堆內(nèi)存中分配空間的(JIT優(yōu)化除外),也知道內(nèi)存分配過程中是線程安全的,那么虛擬機到底是如何保證線程安全的呢?本文就來簡單介紹一下。
1.Java對象的內(nèi)存分配
我們知道,Java是一門面向?qū)ο蟮恼Z言,我們在Java中使用的對象都需要被創(chuàng)建出來,在Java中,創(chuàng)建一個對象的方法有很多種,如使用new、使用反射、使用Clone方法等,但是無論如何,對象在創(chuàng)建過程中,都需要進(jìn)行內(nèi)存分配。
拿最常見的new關(guān)鍵字舉例,當(dāng)我們使用new創(chuàng)建對象后代碼開始運行后,虛擬機執(zhí)行到這條new指令的時候,會先檢查要new的對象對應(yīng)的類是否已被加載,如果沒有被加載則先進(jìn)行類加載。
在類加載檢查通過之后,就需要給對象進(jìn)行內(nèi)存分配了,分配的內(nèi)存主要用來存放對象的實例變量。
在進(jìn)行內(nèi)存分配時,需要根據(jù)對象中的實例變量情況等信息確定需要分配的空間大小,然后從Java堆中劃分出這樣一塊區(qū)域(假設(shè)沒有JIT優(yōu)化)。
根據(jù)JVM使用的垃圾回收器的類型,因其回收算法不同,會導(dǎo)致堆中內(nèi)存分配情況不同。如標(biāo)記-清楚算法回收后的內(nèi)存中會有大量不連續(xù)的內(nèi)存碎片,在給新的對象分配的時候,就需要通過"空閑列表"來確定一塊空閑區(qū)域。(這部分不是本文重點,讀者可以自行學(xué)習(xí)一下。)
無論那種方式,最終都需要確定出一塊內(nèi)存區(qū)域,用于給新建對象分配內(nèi)存。我們知道,對象的內(nèi)存分配過程中,主要是對象的引用指向這個內(nèi)存區(qū)域,然后進(jìn)行初始化操作。
那么問題就來了:
在并發(fā)場景中,如何內(nèi)存分配過程的線程安全性?如果兩個線程先后把對象引用指向了同一個內(nèi)存區(qū)域,怎么辦。
2.TLAB
一般有兩種解決方案:
1、對分配內(nèi)存空間的動作做同步處理,采用CAS機制,配合失敗重試的方式保證更新操作的線程安全性。
2、每個線程在Java堆中預(yù)先分配一小塊內(nèi)存,然后再給對象分配內(nèi)存的時候,直接在自己這塊"私有"內(nèi)存中分配,當(dāng)這部分區(qū)域用完之后,再分配新的"私有"內(nèi)存。
方案1在每次分配時都需要進(jìn)行同步控制,這種是比較低效的。
方案2是HotSpot虛擬機中采用的,這種方案被稱之為TLAB分配,即Thread Local Allocation Buffer。這部分Buffer是從堆中劃分出來的,但是是本地線程獨享的。
這里值得注意的是,我們說TLAB時線程獨享的,但是只是在“分配”這個動作上是線程獨占的,至于在讀取、垃圾回收等動作上都是線程共享的。而且在使用上也沒有什么區(qū)別。
另外,TLAB僅作用于新生代的Eden Space,對象被創(chuàng)建的時候首先放到這個區(qū)域,但是新生代分配不了內(nèi)存的大對象會直接進(jìn)入老年代。因此在編寫Java程序時,通常多個小的對象比大的對象分配起來更加高效。
所以,雖然對象剛開始可能通過TLAB分配內(nèi)存,存放在Eden區(qū),但是還是會被垃圾回收或者被移到Survivor Space、Old Gen等。
不知道大家有沒有想過,我們使用了TLAB之后,在TLAB上給對象分配內(nèi)存時線程獨享的了,這就沒有沖突了,但是,TLAB這塊內(nèi)存自身從堆中劃分出來的過程也可能存在內(nèi)存安全問題啊。
所以,在對于TLAB的分配過程,還是需要進(jìn)行同步控制的。但是這種開銷相比于每次為單個對象劃分內(nèi)存時候?qū)M(jìn)行同步控制的要低的多。
虛擬機是否使用TLAB是可以選擇的,可以通過設(shè)置-XX:+/-UseTLAB參數(shù)來指定。
到此,相信大家對“Java對象的內(nèi)存分配是怎么保證線程安全的”有了更深的了解,不妨來實際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!