深入理解Java虛擬機(jī)-Java內(nèi)存區(qū)域

簡單總結(jié)一下Java虛擬機(jī)運行時數(shù)據(jù)區(qū)


1. 程序計數(shù)器

是一塊較小的內(nèi)存空間,它可以看做是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號指示器;

每條線程都需要有一個獨立的程序計數(shù)器,即“線程私有”;

2. Java虛擬機(jī)棧

Java虛擬機(jī)棧是線程私有的,生命周期與線程相同;

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

3. 本地方法棧

4. Java堆

Java堆是虛擬機(jī)管理內(nèi)存中最大的一塊,是被所有線程共享的一塊內(nèi)存區(qū)域,在虛擬機(jī)啟動時創(chuàng)建。

此內(nèi)存區(qū)域的唯一目的就是存放對象實例。

“幾乎”所有的對象實例都在堆上分配,逃逸分析 標(biāo)量替換。。。

Java堆是垃圾收集器管理的內(nèi)存區(qū)域;

所有線程共享的Java堆中可以劃分出多個線程私有的分配緩沖期(TLAB),以提升對象分配的效率;

無論從什么角度,如何劃分,都不會改變Java堆中存儲內(nèi)容的共性,無論是哪個區(qū)域,存儲的只能是對象的實例,將Java堆細(xì)分的目的只是為了更好地回收內(nèi)存,或者更快地分配內(nèi)存;

Java堆大小設(shè)置 -Xmx 和 -Xms;

5. 方法區(qū)

方法區(qū)與Java堆一樣,是各個線程共享的內(nèi)存區(qū)域,它用于存儲被虛擬機(jī)加載的類型信息,常量,靜態(tài)變量,即時編譯器編譯后的代碼緩存等數(shù)據(jù);

《Java虛擬機(jī)規(guī)范》對方法區(qū)約束十分寬松,除了和Java堆一樣不需要連續(xù)的內(nèi)存和可以選擇固定大小或者可擴(kuò)展外,甚至還可以選擇不實現(xiàn)垃圾收集,這區(qū)域的內(nèi)存回收目標(biāo)是針對常量池到回收和對類型的卸載;

6. 運行時常量池

運行時常量區(qū)是方法區(qū)的一部分。

7. 直接內(nèi)存

直接內(nèi)存并不是虛擬機(jī)運行時數(shù)據(jù)區(qū)的一部分,也不是《java虛擬機(jī)規(guī)范》中定義的內(nèi)存數(shù)據(jù)。但是這部分內(nèi)存也被頻繁地使用,而且也可能導(dǎo)致OOM異常出現(xiàn)。

本機(jī)直接內(nèi)存的分配不會受到Java堆大小的限制,但是,既然是內(nèi)存,則肯定還是會受到本機(jī)總內(nèi)存大小以及處理器尋址空間的限制,一般服務(wù)器管理員配置虛擬機(jī)參數(shù)時,會根據(jù)實際內(nèi)存去設(shè)置-Xmx等參數(shù)信息,但經(jīng)常忽略直接內(nèi)存,使得各個內(nèi)存區(qū)域總和大于物理限制限制(包括物理的和操作系統(tǒng)級的限制),從而導(dǎo)致動態(tài)擴(kuò)展時出現(xiàn)OOM異常。

8. HotSpot虛擬機(jī)對象探究

8.1 對象的創(chuàng)建

new 對象是如何創(chuàng)建的呢?本文討論一下普通Java對象,不包括數(shù)組和Class對象等。

首先檢查new指令參數(shù)是否能在在常量池定位到一個類的符號引用,并檢查這個符號引用代表的類是否被加載,解析和初始化過。如果沒有,則先執(zhí)行相應(yīng)的類加載過程。

類加載之后,再分配內(nèi)存。

“指針碰撞”:Java內(nèi)存絕對規(guī)整(所有使用過的內(nèi)存放一邊,空閑的內(nèi)存放另一邊),中間放著一個指針作為分界點的指示器,那所分配的內(nèi)存就僅僅是把那個指針向空閑空間方向移動一段與內(nèi)存大小相等的距離;

“空閑列表”:內(nèi)存空間不規(guī)整,使用的內(nèi)存與空閑的內(nèi)存交錯在一起,那虛擬機(jī)會維護(hù)一個列表,記錄哪些內(nèi)存是可用的。

具體使用何種方法,有java堆是否規(guī)整決定,而java堆是否規(guī)整則由垃圾收集器的空間壓縮能力決定。

因此,當(dāng)使用Serial,ParNew等帶壓縮整理過程的收集器時,采用指針碰撞,簡單高效;

當(dāng)使用CMS這種基于清除(Sweep)算法的收集時,理論上就只能采用空閑列表了;

如何解決“指針碰撞”線程不安全的問題?

1. 對分配內(nèi)存的動作進(jìn)行同步處理--實際上虛擬機(jī)是采用CAS配上失敗重試的方法保證更新操作的原子性;

2.把內(nèi)存分配的動作按照線程劃分在不同的空間之中進(jìn)行,即每個線程在Java堆中預(yù)先分配一小塊內(nèi)存,稱為本地線程分配緩沖(TLAB),只有本地緩沖區(qū)用完了,分配新的緩沖區(qū)才需要同步鎖定,虛擬機(jī)是否使用TLAB,可以通過-XX:+/-UseTLAB參數(shù)決定;

內(nèi)存分配完,虛擬機(jī)將分配的內(nèi)存空間(但不包括對象頭)都初始化為零值(如果使用了TLAB,這項工作也可以提前至TLAB分配時提前執(zhí)行);

然后虛擬機(jī)對對象進(jìn)行必要的設(shè)置,這些信息放在對象頭中;

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容