運(yùn)行時(shí)數(shù)據(jù)區(qū)
-
程序計(jì)數(shù)器:
- 線程私有(每個(gè)線程都有一塊獨(dú)立的內(nèi)存空間用來(lái)保存該線程的程序計(jì)數(shù)器)
- 指向當(dāng)前線程所執(zhí)行到的位置,字節(jié)碼解釋器就是通過它來(lái)執(zhí)行下一條需要執(zhí)行的指令,分支,循環(huán),跳轉(zhuǎn)等,都是依賴它實(shí)現(xiàn)的;
- 線程切換后,可以恢復(fù)到原來(lái)執(zhí)行的位置繼續(xù)執(zhí)行,也是依賴于它;
- 當(dāng)線程執(zhí)行Native方法時(shí),該計(jì)數(shù)器的值為空;
- 它是唯一一個(gè)沒有OutOfMemoryError的內(nèi)存區(qū)域
-
Java虛擬機(jī)棧
- 線程私有,生命周期與線程相同;
- 它描述的是Java方法執(zhí)行的內(nèi)存模型;
- 每個(gè)方法執(zhí)行時(shí),都會(huì)創(chuàng)建一個(gè)棧幀用于存儲(chǔ)局部變量表,操作數(shù)棧,動(dòng)態(tài)鏈接方法出口等信息,方法從調(diào)用到執(zhí)行完成的過程,就對(duì)應(yīng)著一個(gè)棧幀在虛擬機(jī)中入棧出棧過程;
- 局部變量表存放了編譯器可知的各種基本數(shù)據(jù)類型,對(duì)象引用和returnAddress類型;
- 局部變量的內(nèi)存空間分配在便宜期間完成,運(yùn)行期間不會(huì)改變大小;
- 該內(nèi)存區(qū)域會(huì)拋出StackOverflowError(棧深度)和OutOfMemoryError。
-
本地方法棧
- 為虛擬機(jī)中使用到的Native方法服務(wù);
- 虛擬機(jī)規(guī)范中沒有強(qiáng)制規(guī)定實(shí)現(xiàn),所以不同虛擬機(jī)可以有不同實(shí)現(xiàn),但是與虛擬機(jī)棧的作用類似。
-
Java堆
- 所有線程共享,虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建;
- 作用:存放對(duì)象實(shí)例;
- 是GC的主要區(qū)域
- 分為新生代(Eden區(qū),F(xiàn)rom Survivor區(qū),To Survivor區(qū))和老年代;
-
方法區(qū)(元數(shù)據(jù)區(qū))
- 各個(gè)線程共享,存儲(chǔ)加載的類的信息,常量,靜態(tài)變量,即時(shí)編譯后的代碼等數(shù)據(jù);
- 這里的內(nèi)存回收:常量池的回收和類型的卸載;
- 會(huì)拋出OutOfMemoryError異常
- 運(yùn)行時(shí)常量池:用于存放編譯器生成的各種字面量和符號(hào)引用,在編譯時(shí)和運(yùn)行時(shí)都可以加入內(nèi)容;
-
直接內(nèi)存
- 它不是運(yùn)行時(shí)數(shù)據(jù)區(qū)的一部分,而是服務(wù)于NIO類的,直接通過Native操作分配堆外內(nèi)存的區(qū)域;
- 它受制于本機(jī)物理內(nèi)存的大小。
HotSpot虛擬機(jī)中的對(duì)象
對(duì)象的創(chuàng)建
- 當(dāng)遇到new指令時(shí),首先檢查該符號(hào)引用代表的類是否已經(jīng)經(jīng)過加載,鏈接和初始化,若未進(jìn)行,則先執(zhí)行類的加載;
-
為新生對(duì)象分配內(nèi)存(取決于內(nèi)存是否規(guī)整)
- 指針碰撞:Java堆中內(nèi)存絕對(duì)規(guī)整,其間通過一個(gè)指針作為分界點(diǎn)的指示器,分配內(nèi)存時(shí),向空閑那端移動(dòng)一段與對(duì)象大小相等的距離;
- 空閑列表:Java堆中內(nèi)存不規(guī)整,哪塊內(nèi)存是可用的記錄在"空閑列表"中,分配時(shí),從空閑列表中找到一塊足夠大的空間劃分給對(duì)象實(shí)例并更新列表上的記錄;
注意:因?yàn)榉峙鋬?nèi)存是一個(gè)非常頻繁的操作,所以為了保證線程安全:- 同步處理:CAS+失敗重試來(lái)保證原子性
- 將內(nèi)存分配動(dòng)作劃分到不同的空間中進(jìn)行:堆中預(yù)先為每個(gè)線程預(yù)留一塊空間,稱為本地線程分配緩存(TLAB),只有TLAB用完后再分配新的TLAB時(shí),才需要同步;
- 初始化內(nèi)存,將內(nèi)存空間全部初始化為零值(不包括對(duì)象頭)
-
設(shè)置對(duì)象的必要信息(對(duì)象頭)
- 對(duì)象的歸屬,如何找到類的元數(shù)據(jù)信息
- 對(duì)象的哈希值
- GC分代年齡
- 執(zhí)行程序定義的初始化方法
對(duì)象的內(nèi)存布局
-
對(duì)象頭(Mark Word)
說明:會(huì)根據(jù)對(duì)象的狀態(tài)復(fù)用自己的存儲(chǔ)空間,可以根據(jù)標(biāo)志位來(lái)判斷,它主要由以組成- 對(duì)象的運(yùn)行時(shí)數(shù)據(jù)
- hashCode
- GC分代年齡
- 鎖的各種信息
- 類型指針(指向它所屬的Class)
- 對(duì)于數(shù)組,還有一塊用于記錄數(shù)組長(zhǎng)度的數(shù)據(jù)(因?yàn)閷?duì)象可以從元數(shù)據(jù)中知道它占用的空間大小,而數(shù)組無(wú)法確定)
- 對(duì)象的運(yùn)行時(shí)數(shù)據(jù)
- 實(shí)例數(shù)據(jù):對(duì)象實(shí)例存儲(chǔ)數(shù)據(jù)的有效信息,即程序中定義的字段信息。其中包含了其自己的數(shù)據(jù)以及從父類繼承的數(shù)據(jù),存儲(chǔ)順序受分配策略和字段定義順序影響。
- 對(duì)齊填充:占位符,用來(lái)保證對(duì)象的起始地址始終是8字節(jié)的整數(shù)倍。
對(duì)象的訪問定位
Java通過棧上的引用來(lái)操作堆上的具體對(duì)象,目前的訪問方式有如下兩種:
- 句柄
- 堆中劃分出一塊內(nèi)存作為句柄池,引用指向句柄地址(對(duì)象實(shí)例數(shù)據(jù)和類型數(shù)據(jù)的具體地址信息)
- 優(yōu)點(diǎn):對(duì)象改變時(shí),只需改變句柄中實(shí)例指針即可,棧中引用不需要更改
- 缺點(diǎn):訪問對(duì)象需要經(jīng)過兩次訪問,速度慢
- 直接指針
- 引用直接存放對(duì)象地址,訪問速度快(HotSpot使用)