在以前的文章中,咱們介紹了使用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個字節是填充位。指針
那麼這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/最通俗的解讀,最深入的乾貨,最簡潔的教程,衆多你不知道的小技巧等你來發現!
歡迎關注個人公衆號:「程序那些事」,懂技術,更懂你!