在HotSpot虛擬機(jī)中坐搔,一個對象內(nèi)部分為三個區(qū)域:對象頭(Header)、實(shí)例數(shù)據(jù)(Instance Data)敬矩、對齊填充(Padding)概行。
對象頭
對象頭內(nèi)部又分為三部分:標(biāo)記字(Mark Word)、類型指針(Class Pointer)弧岳、數(shù)組長度(數(shù)組類型的對象才有)凳忙。
- 標(biāo)記字:用于存儲對象自身的運(yùn)行時數(shù)據(jù),如:HashCode禽炬、GC分代年齡涧卵、鎖狀態(tài)標(biāo)志、線程持有的鎖瞎抛、偏向線程ID艺演、偏向時間戳等。32位JVM中Mark Word大小為4字節(jié)(Byte)桐臊,64位JVM中為8字節(jié)胎撤。
- 類型指針:是對象指向它的類元數(shù)據(jù)的指針(即元空間中該類的內(nèi)存地址),虛擬機(jī)通過這個指針來確定這個對象是哪個類的實(shí)例断凶。32位JVM中Class Pointer大小為4字節(jié)伤提,64位JVM中為8字節(jié)。從JDK6 update14開始认烁,64位JVM便正式支持了-XX:+UseCompressedOops這個參數(shù)(默認(rèn)開啟)肿男,可以壓縮類型指針的大小為4字節(jié),起到節(jié)約內(nèi)存占用的目的却嗡。
- 數(shù)組長度:數(shù)組的長度舶沛,非數(shù)組對象則沒有這部分。32位和64位JVM中數(shù)組長度大小都為4字節(jié)窗价。
實(shí)例數(shù)據(jù)
對象的成員變量如庭,包括父類中定義的成員變量『掣郏基本類型成員變量占內(nèi)存大衅核:boolean和byte(1字節(jié))、short和char(2字節(jié))帝牡、int和float(4字節(jié))往毡、long和double(8字節(jié))。非基本類型的成員變量存放的是各個成員變量在堆或元空間中的內(nèi)存地址靶溜,32位JVM中大小為4字節(jié)开瞭,64位JVM中關(guān)閉指針壓縮時為8字節(jié)懒震,開啟指針壓縮時為4字節(jié)。
對齊填充
由于虛擬機(jī)要求對象起始地址必須是8字節(jié)的整數(shù)倍惩阶,所以當(dāng)對象頭和實(shí)例數(shù)據(jù)的大小之和不是8字節(jié)的整數(shù)倍時挎狸,就會有對齊填充部分,用于字節(jié)對齊断楷。
計(jì)算對象大小
Object o = new Object();
這樣一個沒有任何成員變量的對象锨匆,占多少內(nèi)存?
- 32位:Mark Word(4B) + Class Pointer(4B) + ArrayLength(0B) + Instance Data(0B) + Padding(0B) = 8B
- 64位關(guān)閉壓縮類型指針:Mark Word(8B) + Class Pointer(8B) + ArrayLength(0B) + Instance Data(0B) + Padding(0B) = 16B
- 64位開啟壓縮類型指針:Mark Word(8B) + Class Pointer(4B) + ArrayLength(0B) + Instance Data(0B) + Padding(4B) = 16B
借助JOL(JAVA Object Layout)工具冬筒,可以直觀地觀察JAVA對象的內(nèi)部結(jié)構(gòu)恐锣,以下測試均在64位JVM上運(yùn)行。
首先添加依賴:
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.11</version>
</dependency>
運(yùn)行示例代碼:
import org.openjdk.jol.info.ClassLayout;
public class TestJol {
public static void main(String[] args) {
Object o = new Object();
System.out.println(ClassLayout.parseInstance(o).toPrintable());
}
}
程序輸出:
java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
前兩行是Mark Word舞痰,共8字節(jié)土榴,第三行是類型指針,4字節(jié)响牛,第四行是填充對齊玷禽,4字節(jié)。
關(guān)閉類型指針壓縮(-XX:-UseCompressedOops)后再次運(yùn)行呀打,程序輸出:
java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 00 1c 83 1b (00000000 00011100 10000011 00011011) (461577216)
12 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
可以看出兩種情況下矢赁,該對象占用的內(nèi)存大小(Instance size)都是16字節(jié)贬丛,區(qū)別在于關(guān)閉類型指針壓縮后撩银,類型指針的大小變成了8字節(jié),相應(yīng)的對象頭的大小變成了16字節(jié)豺憔,整個對象的大小也是16字節(jié)额获,剛好是8的整數(shù)倍,所以沒有了填充對齊的空間恭应。