「每日五分鐘,玩轉JVM」:對象內存佈局

概覽

一個對象根據不一樣狀況能夠被劃分紅兩種狀況,當對象是一個非數組對象的時候,對象頭,實例數據,對齊填充在內存中三分天下,而數組對象中在對象頭中多了一個用於描述數組對象長度的部分算法

image-20190823221858767

對象頭

對象頭分爲兩部分,第一部分稱之爲**"Mark Word",第二部分是用於獲取該對象類型的類型指針**,若是是數組對象還包括記錄數組長度的數據。編程

在不一樣的操做系統中,這些區域所佔的內存也不一樣,在32位的系統中,MarkWord佔用32bit的空間(也就是4字節)。類型指針和數組長度數據同樣合做佔用32bit的空間。數組

在64位的操做系統中,MarkWord佔用64bit的空間,類型指針在不開啓指針壓縮(CompressedOOPs)的狀況下是64bit(8 byte),而在開啓指針壓縮的狀況下,僅剩32bit(4 byte)數據結構

image-20190823225703812

Mark Word

這一部分存儲的是對象自身的運行時數據,這一起內容的數據結構並不固定,它會根據對象的狀態複用本身的存儲空間,併發

image-20190823230559543

這是摘自markOop.hpp文件中的片斷,其中表示了對象的如下五種狀態:oop

標誌位 偏向鎖標識位 狀態
01 0 無鎖
01 1 偏向鎖
00 輕量級鎖
10 重量級鎖
11 GC Mark

咱們接下來接着去看MarkWord的結構:操作系統

image-20190823231212050

在這裏咱們能夠看到,初始化的時候只是定義了無鎖和偏向鎖狀態的結構(上半部分是沒有開啓COOPs-指針壓縮的結構,下半部分是開啓了指針壓縮的結構),線程

當處於輕量級鎖、重量級鎖時,記錄的對象指針,根據JVM的說明,此時認爲指針仍然是64位,最低兩位假定爲0;當處於偏向鎖時,記錄的爲得到偏向鎖的線程指針,該指針也是64位;3d

image-20190823231522289

更多的內容咱們就再也不這裏擴展了,根據反饋的狀況,我會在後面併發編程中單開一篇來聊聊鎖的進化之路。指針

類型指針

這個東西有時候會用到去肯定該對象屬於哪一個類的實例,也有用不到的時候,這個要根據不一樣的虛擬機對於對象的定位實現算法的選擇來進行(好比HotSpot JVM就使用該類型指針去獲取該對象類型數據)

實例數據

實例數據是對象真正存儲的有效信息,也是在程序代碼中所定義的各類類型的字段內容,這裏的字段內容不單單包括當前類的字段,也包括他的父類中所定義的字段。

這部分的存儲規則遵循虛擬機分配策略參數和字段在Java源碼中的定義順序,HotSpot JVM默認的分配策略是long/double, int,short/char,byte/boolean,oops(普通對象指針,Ordinary Object Pointers)也能夠理解爲reference,關於指針壓縮咱們下節去說。

這裏須要注意,在父類中定義的變量會出如今子類前,可是咱們能夠經過將CompactFileds參數設置爲true,將子類中較小的變量插入到父類大變量的空隙中。

image-20190824120847519

對齊填充

這部份內容並非必須存在的,由於Hot Spot JVM中規定了對象的大小必須是8字節的整數倍,在C/C++中相似的功能被稱之爲內存對齊,內存空間都是按照 byte 劃分的,從理論上講彷佛對任何類型的變量的訪問能夠從任何地址開始,但實際狀況是在訪問特定類型變量的時候常常在特定的內存地址訪問,這就須要各類類型數據按照必定的規則在空間上排列,而不是順序的一個接一個的排放,這就是對齊。

內存對齊遵循兩個規則:

  • 假設第一個成員的起始地址爲0,每一個成員的起始地址(startpos)必須是其數據類型所佔空間大小的整數倍

  • 結構體的最終大小必須是其成員(基礎數據類型成員)裏最大成員所佔大小的整數倍。

這裏也就不難理解爲何JVM規定對象的大小必須是8字節的整數倍了,由於在64位系統下(不開啓指針壓縮),對象中存在不少佔用8 byte的數據類型。可是同時也存在一些4 byte的數據類型,這時咱們的Padding就起到了做用,去補充不滿8 byte的部分,湊齊8的整數倍。

image-20190824121114907

公衆號

相關文章
相關標籤/搜索