淺談java object對(duì)象在heap中的結(jié)構(gòu)
java.lang.Object大家應(yīng)該都很熟悉了,Object是java中一切對(duì)象的鼻祖。
接下來(lái)我們來(lái)對(duì)這個(gè)java對(duì)象的鼻祖進(jìn)行一個(gè)詳細(xì)的解剖分析,從而理解JVM的深層次的秘密。
工具當(dāng)然是使用JOL:
@Slf4jpublic class JolUsage { @Test public void useJol(){log.info('{}', VM.current().details());log.info('{}', ClassLayout.parseClass(Object.class).toPrintable());log.info('{}', ClassLayout.parseInstance(new Object()).toPrintable()); }}
代碼很簡(jiǎn)單,我們打印JVM的信息,Object class和一個(gè)新的Object實(shí)例的信息。
看下輸出:
[main] INFO com.flydean.JolUsage - # Running 64-bit HotSpot VM.
# Using compressed oop with 3-bit shift.
# Using compressed klass with 3-bit shift.
# WARNING | Compressed references base/shifts are guessed by the experiment!
# WARNING | Therefore, computed addresses are just guesses, and ARE NOT RELIABLE.
# WARNING | Make sure to attach Serviceability Agent to get the reliable addresses.
# Objects are 8 bytes aligned.
# Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
10:27:32.311 [main] INFO com.flydean.JolUsage - java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 12 (object header) N/A
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
10:27:32.312 [main] INFO com.flydean.JolUsage - java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 86 06 00 00 (10000110 00000110 00000000 00000000) (1670)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total3
從上面的結(jié)果我們知道,在64位的JVM中,一個(gè)Object實(shí)例是占用16個(gè)字節(jié)。
因?yàn)镺bject對(duì)象中并沒(méi)有其他對(duì)象的引用,所以我們看到Object對(duì)象只有一個(gè)12字節(jié)的對(duì)象頭。剩下的4個(gè)字節(jié)是填充位。
Object對(duì)象頭那么這12字節(jié)的對(duì)象頭是做什么用的呢?
如果想要深入了解這12字節(jié)的對(duì)象頭,當(dāng)然是要去研讀一下JVM的源碼:src/share/vm/oops/markOop.hpp。
有興趣的小伙伴可以去看看。如果沒(méi)有興趣,沒(méi)關(guān)系,這里給大家一個(gè)張總結(jié)的圖:
javaObject對(duì)象的對(duì)象頭大小根據(jù)你使用的是32位還是64位的虛擬機(jī)的不同,稍有變化。這里我們使用的是64位的虛擬機(jī)為例。
Object的對(duì)象頭,分為兩部分,第一部分是Mark Word,用來(lái)存儲(chǔ)對(duì)象的運(yùn)行時(shí)數(shù)據(jù)比如:hashcode,GC分代年齡,鎖狀態(tài),持有鎖信息,偏向鎖的thread ID等等。
在64位的虛擬機(jī)中,Mark Word是64bits,如果是在32位的虛擬機(jī)中Mark Word是32bits。
第二部分就是Klass Word,Klass Word是一個(gè)類(lèi)型指針,指向class的元數(shù)據(jù),JVM通過(guò)Klass Word來(lái)判斷該對(duì)象是哪個(gè)class的實(shí)例。
且慢!
有的小伙伴可能發(fā)現(xiàn)了問(wèn)題,之前我們用JOL解析Object對(duì)象的時(shí)候,Object head大小是12字節(jié),也就是96bits,這里怎么寫(xiě)的是128bits?
沒(méi)錯(cuò),如果沒(méi)有開(kāi)啟COOPs就是128bits,如果開(kāi)啟了COOPs,那么Klass Word的大小就從64bits降到了32bits。
還記得我們之前講的COOPs嗎?
COOPs就是壓縮對(duì)象指針技術(shù)。
對(duì)象指針用來(lái)指向一個(gè)對(duì)象,表示對(duì)該對(duì)象的引用。通常來(lái)說(shuō)在64位機(jī)子上面,一個(gè)指針占用64位,也就是8個(gè)字節(jié)。而在32位機(jī)子上面,一個(gè)指針占用32位,也就是4個(gè)字節(jié)。
實(shí)時(shí)上,在應(yīng)用程序中,這種對(duì)象的指針是非常非常多的,從而導(dǎo)致如果同樣一個(gè)程序,在32位機(jī)子上面運(yùn)行和在64位機(jī)子上面運(yùn)行占用的內(nèi)存是完全不同的。64位機(jī)子內(nèi)存使用可能是32位機(jī)子的1.5倍。
而壓縮對(duì)象指針,就是指把64位的指針壓縮到32位。
怎么壓縮呢?64位機(jī)子的對(duì)象地址仍然是64位的。壓縮過(guò)的32位存的只是相對(duì)于heap base address的位移。
我們使用64位的heap base地址+ 32位的地址位移量,就得到了實(shí)際的64位heap地址。
對(duì)象指針壓縮在Java SE 6u23 默認(rèn)開(kāi)啟。在此之前,可以使用-XX:+UseCompressedOops來(lái)開(kāi)啟。
數(shù)組對(duì)象頭java中有一個(gè)非常特別的對(duì)象叫做數(shù)組,數(shù)組的對(duì)象頭和Object有什么區(qū)別嗎?
我們用JOL再看一次:
log.info('{}',ClassLayout.parseClass(byte[].class).toPrintable());log.info('{}',ClassLayout.parseInstance('www.flydean.com'.getBytes()).toPrintable());
上面的例子中我們分別解析了byte數(shù)組的class和byte數(shù)組的實(shí)例:
10:27:32.396 [main] INFO com.flydean.JolUsage - [B object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 16 (object header) N/A
16 0 byte [B.<elements> N/A
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
10:27:32.404 [main] INFO com.flydean.JolUsage - [B 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) 22 13 07 00 (00100010 00010011 00000111 00000000) (463650)
12 4 (object header) 0f 00 00 00 (00001111 00000000 00000000 00000000) (15)
16 15 byte [B.<elements> N/A
31 1 (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 0 bytes internal + 1 bytes external = 1 bytes total
看到區(qū)別了嗎?我們發(fā)現(xiàn)數(shù)組的對(duì)象頭是16字節(jié),比普通對(duì)象的對(duì)象頭多出了4個(gè)字節(jié)。這4個(gè)字節(jié)就是數(shù)組的長(zhǎng)度。
整個(gè)對(duì)象的結(jié)構(gòu)好了,寫(xiě)到這里我們來(lái)總結(jié)一下,java對(duì)象的結(jié)構(gòu)可以分為普通java對(duì)象和數(shù)組對(duì)象兩種:
數(shù)組對(duì)象在對(duì)象頭中多了一個(gè)4字節(jié)的長(zhǎng)度字段。
大家看到最后的字節(jié)是padding填充字節(jié),為什么要填充呢?
因?yàn)镴VM是以8字節(jié)為單位進(jìn)行對(duì)其的,如果不是8字節(jié)的整數(shù)倍,則需要補(bǔ)全。
以上就是淺談java object對(duì)象在heap中的結(jié)構(gòu)的詳細(xì)內(nèi)容,更多關(guān)于java object對(duì)象在heap中的結(jié)構(gòu)的資料請(qǐng)關(guān)注好吧啦網(wǎng)其它相關(guān)文章!
相關(guān)文章:
1. xml中的空格之完全解說(shuō)2. ASP中解決“對(duì)象關(guān)閉時(shí),不允許操作。”的詭異問(wèn)題……3. msxml3.dll 錯(cuò)誤 800c0019 系統(tǒng)錯(cuò)誤:-2146697191解決方法4. html小技巧之td,div標(biāo)簽里內(nèi)容不換行5. XML入門(mén)的常見(jiàn)問(wèn)題(四)6. ASP中if語(yǔ)句、select 、while循環(huán)的使用方法7. 匹配模式 - XSL教程 - 48. 解決ASP中http狀態(tài)跳轉(zhuǎn)返回錯(cuò)誤頁(yè)的問(wèn)題9. WML語(yǔ)言的基本情況10. CSS3中Transition屬性詳解以及示例分享
