JVM-對(duì)象內(nèi)存布局

jvm-對(duì)象內(nèi)存布局

對(duì)象內(nèi)存結(jié)構(gòu)概述

對(duì)象的創(chuàng)建過(guò)程:

  1. jvm將對(duì)象所在的class文件加載到方法區(qū)中
  2. jvm讀取main方法入口,將main方法入棧,執(zhí)行創(chuàng)建對(duì)象代碼
  3. 在main方法的棧內(nèi)存中分配對(duì)象的引用,在堆中分配內(nèi)存放入創(chuàng)建的對(duì)象,并將棧中的引用指向堆中的對(duì)象

堆內(nèi)存中的對(duì)象由3部分組成:

  1. 對(duì)象頭 header
  2. 實(shí)例數(shù)據(jù) instance data
  3. 對(duì)齊填充字節(jié) padding

對(duì)象頭

對(duì)象頭存儲(chǔ)的是對(duì)象在運(yùn)行時(shí)狀態(tài)的相關(guān)信息、指向該對(duì)象所屬類(lèi)的元數(shù)據(jù)的指針,如果對(duì)象是數(shù)組對(duì)象那么還會(huì)額外存儲(chǔ)對(duì)象的數(shù)組長(zhǎng)度

對(duì)象頭的組成

普通對(duì)象:

  1. 標(biāo)記字 mark word
  2. 指針類(lèi)型 klass pointer

數(shù)組對(duì)象:

  1. 標(biāo)記字 mark word
  2. 指針類(lèi)型 klass pointer
  3. 數(shù)組長(zhǎng)度

mark word

在對(duì)象頭中,mark word 一共有64個(gè)bit,用于存儲(chǔ)對(duì)象自身的運(yùn)行時(shí)數(shù)據(jù),標(biāo)記對(duì)象處于以下5種狀態(tài)中的某一種:

(此處應(yīng)有圖)

在mark word中,鎖(lock)標(biāo)志位占用2個(gè)bit,結(jié)合1個(gè)bit偏向鎖(biased_lock)標(biāo)志位,這樣通過(guò)倒數(shù)的3位,就能用來(lái)標(biāo)識(shí)當(dāng)前對(duì)象持有的鎖的狀態(tài),并判斷出其余位存儲(chǔ)的是什么信息。


基于mark word的鎖升級(jí)流程:

無(wú)鎖/可偏向

鎖對(duì)象剛創(chuàng)建時(shí),沒(méi)有任何線程競(jìng)爭(zhēng),對(duì)象處于無(wú)鎖狀態(tài)。在上面打印的空對(duì)象的內(nèi)存布局中,根據(jù)大小端,最后3位001,表示處于無(wú)鎖態(tài),并且處于不可偏向狀態(tài)。這是因?yàn)樵趈dk中偏向鎖存在延遲4秒啟動(dòng),也就是說(shuō)在jvm啟動(dòng)后4秒后創(chuàng)建的對(duì)象才會(huì)開(kāi)啟偏向鎖,我們通過(guò)jvm參數(shù)-XX:BiasedLockingStartupDelay=0取消這個(gè)延遲時(shí)間,這時(shí)最后3位為101,表示當(dāng)前對(duì)象的鎖沒(méi)有被持有,并且處于可被偏向狀態(tài)。

偏向鎖

在沒(méi)有線程競(jìng)爭(zhēng)的條件下,第一個(gè)獲取鎖的線程通過(guò)CAS將自己的threadId寫(xiě)入到該對(duì)象的mark word中,若后續(xù)該線程再次獲取鎖,需要比較當(dāng)前線程threadId和對(duì)象mark word中的threadId是否一致,如果一致那么可以直接獲取,并且鎖對(duì)象始終保持對(duì)該線程的偏向,也就是說(shuō)偏向鎖不會(huì)主動(dòng)釋放。
偏向鎖的CAS環(huán)節(jié)在修改thread id

無(wú)鎖和偏向鎖狀態(tài)時(shí),偏向鎖標(biāo)記位是有值的,所以看后三位

輕量級(jí)鎖

當(dāng)兩個(gè)或以上線程交替獲取鎖,但并沒(méi)有在對(duì)象上并發(fā)的獲取鎖時(shí),偏向鎖升級(jí)為輕量級(jí)鎖。在此階段,線程采取CAS的自旋方式嘗試獲取鎖,避免阻塞線程造成的cpu在用戶態(tài)和內(nèi)核態(tài)間轉(zhuǎn)換的消耗。
輕量級(jí)鎖的CAS環(huán)節(jié)在自旋獲取鎖

輕量級(jí)鎖和重量級(jí)鎖時(shí),已不需要偏向鎖標(biāo)記,所以只有后兩位

綜合上面3個(gè)階段,整個(gè)加鎖狀態(tài)的變化流程如下:

  1. 主線程首先對(duì)user對(duì)象加鎖,首次加鎖為101偏向鎖
  2. 子線程等待主線程釋放鎖后,對(duì)user對(duì)象加鎖,這時(shí)將偏向鎖升級(jí)為00輕量級(jí)鎖
  3. 輕量級(jí)鎖解鎖后,user對(duì)象無(wú)線程競(jìng)爭(zhēng),恢復(fù)為001無(wú)鎖態(tài),并且處于不可偏向狀態(tài)。如果之后有線程再嘗試獲取user對(duì)象的鎖,會(huì)直接加輕量級(jí)鎖,而不是偏向鎖

重量級(jí)鎖

當(dāng)兩個(gè)或以上線程并發(fā)的在同一個(gè)對(duì)象上進(jìn)行同步時(shí),為了避免無(wú)用自旋消耗cpu,輕量級(jí)鎖會(huì)升級(jí)成重量級(jí)鎖。這時(shí)mark word中的指針指向的是monitor對(duì)象(也被稱為管程或監(jiān)視器鎖)的起始地址。在兩個(gè)線程同時(shí)競(jìng)爭(zhēng)user對(duì)象的鎖時(shí),會(huì)升級(jí)為10重量級(jí)鎖。

mark word中的其他信息

hashcode

無(wú)鎖態(tài)下的hashcode采用了延遲加載技術(shù),在第一次調(diào)用hashCode()方法時(shí)才會(huì)計(jì)算寫(xiě)入。

只有在調(diào)用沒(méi)有被重寫(xiě)的Object.hashCode()方法或System.identityHashCode(Object)方法才會(huì)寫(xiě)入mark word,執(zhí)行用戶自定義的hashCode()方法不會(huì)被寫(xiě)入。

當(dāng)對(duì)象被加鎖后,mark word中就沒(méi)有足夠空間來(lái)保存hashCode了,這時(shí)hashcode會(huì)被移動(dòng)到重量級(jí)鎖的Object Monitor中。

epoch

偏向鎖的時(shí)間戳

分代年齡(age)

在jvm的垃圾回收過(guò)程中,每當(dāng)對(duì)象經(jīng)過(guò)一次Young GC,年齡都會(huì)加1,這里4位來(lái)表示分代年齡最大值為15,這也就是為什么對(duì)象的年齡超過(guò)15后會(huì)被移到老年代的原因。在啟動(dòng)時(shí)可以通過(guò)添加參數(shù)來(lái)改變年齡閾值-XX:MaxTenuringThreshold,當(dāng)設(shè)置的閾值超過(guò)15時(shí),啟動(dòng)時(shí)會(huì)報(bào)錯(cuò)。

Klass Pointer 類(lèi)型指針

Klass Pointer是一個(gè)指向方法區(qū)中Class信息的指針,虛擬機(jī)通過(guò)這個(gè)指針確定該對(duì)象屬于哪個(gè)類(lèi)的實(shí)例。在64位的JVM中,支持指針壓縮功能,根據(jù)是否開(kāi)啟指針壓縮,Klass Pointer占用的大小將會(huì)不同:

  1. 未開(kāi)啟指針壓縮時(shí),類(lèi)型指針占用8B (64bit)
  2. 開(kāi)啟指針壓縮情況下,類(lèi)型指針占用4B (32bit)

在jdk6之后的版本中,指針壓縮是被默認(rèn)開(kāi)啟的

實(shí)例數(shù)據(jù)

實(shí)例數(shù)據(jù)存儲(chǔ)的是對(duì)象的真正有效數(shù)據(jù),也就是各個(gè)屬性字段的值,如果在擁有父類(lèi)的情況下,還會(huì)包含父類(lèi)的字段。字段的存儲(chǔ)順序會(huì)受到數(shù)據(jù)類(lèi)型長(zhǎng)度、以及虛擬機(jī)的分配策略的影響

對(duì)齊填充字節(jié)

在java對(duì)象中,需要對(duì)齊填充字節(jié)的原因是,64位的jvm中對(duì)象的大小被要求向8字節(jié)對(duì)齊,因此當(dāng)對(duì)象的長(zhǎng)度不足8字節(jié)的整數(shù)倍時(shí),需要在對(duì)象中進(jìn)行填充操作。注意圖中對(duì)齊填充部分使用了虛線,這是因?yàn)樘畛渥止?jié)并不是固定存在的部分,這點(diǎn)在后面計(jì)算對(duì)象大小時(shí)具體進(jìn)行說(shuō)明

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

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

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