JVM系列之:詳解java object對象在heap中的結構

簡介

在以前的文章中,咱們介紹了使用JOL這一神器來解析java類或者java實例在內存中佔用的空間地址。java

今天,咱們會更進一步,剖析一下在以前文章中沒有講解到的更深層次的細節。一塊兒來看看吧。segmentfault

對象和其隱藏的祕密

java.lang.Object你們應該都很熟悉了,Object是java中一切對象的鼻祖。數組

接下來咱們來對這個java對象的鼻祖進行一個詳細的解剖分析,從而理解JVM的深層次的祕密。jvm

工具固然是使用JOL:工具

@Slf4j
public 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());
    }
}

代碼很簡單,咱們打印JVM的信息,Object class和一個新的Object實例的信息。oop

看下輸出:url

[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 total

從上面的結果咱們知道,在64位的JVM中,一個Object實例是佔用16個字節。 spa

由於Object對象中並無其餘對象的引用,因此咱們看到Object對象只有一個12字節的對象頭。剩下的4個字節是填充位。指針

Object對象頭

那麼這12字節的對象頭是作什麼用的呢?code

若是想要深刻了解這12字節的對象頭,固然是要去研讀一下JVM的源碼:src/share/vm/oops/markOop.hpp。

有興趣的小夥伴能夠去看看。若是沒有興趣,不要緊,這裏給你們一個張總結的圖:

javaObject對象的對象頭大小根據你使用的是32位仍是64位的虛擬機的不一樣,稍有變化。這裏咱們使用的是64位的虛擬機爲例。

Object的對象頭,分爲兩部分,第一部分是Mark Word,用來存儲對象的運行時數據好比:hashcode,GC分代年齡,鎖狀態,持有鎖信息,偏向鎖的thread ID等等。

在64位的虛擬機中,Mark Word是64bits,若是是在32位的虛擬機中Mark Word是32bits。

第二部分就是Klass Word,Klass Word是一個類型指針,指向class的元數據,JVM經過Klass Word來判斷該對象是哪一個class的實例。

且慢!

有的小夥伴可能發現了問題,以前咱們用JOL解析Object對象的時候,Object head大小是12字節,也就是96bits,這裏怎麼寫的是128bits?

沒錯,若是沒有開啓COOPs就是128bits,若是開啓了COOPs,那麼Klass Word的大小就從64bits降到了32bits。

還記得咱們以前講的COOPs嗎?

COOPs就是壓縮對象指針技術。

對象指針用來指向一個對象,表示對該對象的引用。一般來講在64位機子上面,一個指針佔用64位,也就是8個字節。而在32位機子上面,一個指針佔用32位,也就是4個字節。

實時上,在應用程序中,這種對象的指針是很是很是多的,從而致使若是一樣一個程序,在32位機子上面運行和在64位機子上面運行佔用的內存是徹底不一樣的。64位機子內存使用多是32位機子的1.5倍。

而壓縮對象指針,就是指把64位的指針壓縮到32位。

怎麼壓縮呢?64位機子的對象地址仍然是64位的。壓縮過的32位存的只是相對於heap base address的位移。

咱們使用64位的heap base地址+ 32位的地址位移量,就獲得了實際的64位heap地址。

對象指針壓縮在Java SE 6u23 默認開啓。在此以前,可使用-XX:+UseCompressedOops來開啓。

數組對象頭

java中有一個很是特別的對象叫作數組,數組的對象頭和Object有什麼區別嗎?

咱們用JOL再看一次:

log.info("{}",ClassLayout.parseClass(byte[].class).toPrintable());

log.info("{}",ClassLayout.parseInstance("www.flydean.com".getBytes()).toPrintable());

上面的例子中咱們分別解析了byte數組的class和byte數組的實例:

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

看到區別了嗎?咱們發現數組的對象頭是16字節,比普通對象的對象頭多出了4個字節。這4個字節就是數組的長度。

整個對象的結構

好了,寫到這裏咱們來總結一下,java對象的結構能夠分爲普通java對象和數組對象兩種:

數組對象在對象頭中多了一個4字節的長度字段。

你們看到最後的字節是padding填充字節,爲何要填充呢?

由於JVM是以8字節爲單位進行對其的,若是不是8字節的整數倍,則須要補全。

本文連接: http://www.flydean.com/jvm-java-object-in-heap/

最通俗的解讀,最深入的乾貨,最簡潔的教程,衆多你不知道的小技巧等你來發現!

歡迎關注個人公衆號:「程序那些事」,懂技術,更懂你!

相關文章
相關標籤/搜索