- 對象在內(nèi)存中的存儲可以分為三個區(qū)域:對象頭(Header)默怨,實例數(shù)據(jù)(Instance Data)和對齊填充(Padding)码撰。
頭對象
-
對于不同的對象類型疯特,虛擬機存儲的長度也不同。如果對象是數(shù)組類型眉枕,則虛擬機用3個字寬存儲對象頭械哟,如果對象是非數(shù)組類型疏之,則用2字寬存儲對象頭。在32位虛擬機中戒良,1字寬等于4字節(jié)体捏,既32bit。入下圖所示:
長度 內(nèi)容 說明 32/64bit Mark Word 存儲對象的hashcode和鎖信息等 32/64bit Class MetaData Address 存儲到對象類型數(shù)據(jù)的指針 32/64bit Array length 數(shù)組長度(如果當(dāng)前是數(shù)組)
Mark Word
Mark Word存儲的是對象自身的運行數(shù)據(jù)糯崎,如哈希碼,GC分代年齡河泳,鎖狀態(tài)標(biāo)記沃呢,偏向鎖ID,偏向時間戳等拆挥。
由于32bit薄霜,64bit儲存的數(shù)據(jù)不夠多,為了存儲更多信息纸兔,Mark Word被設(shè)計成非固定的存儲結(jié)構(gòu)惰瓜,其存儲的數(shù)據(jù)會隨著鎖標(biāo)志位的變化而變化。
-
32bit虛擬機中的Mark Word存儲結(jié)構(gòu)
image -
64bit虛擬機中的Mark Word存儲結(jié)構(gòu)
image 輕量級鎖和偏向鎖是Java 6 對 synchronized 鎖進行優(yōu)化后新增加的汉矿,這里并不進行詳細介紹崎坊。
類型指針
- 用來指向?qū)ο髮?yīng)的Class對象(其對應(yīng)的元數(shù)據(jù)對象)的內(nèi)存地址。在32位系統(tǒng)占4字節(jié)洲拇,在64位系統(tǒng)中占8字節(jié)奈揍;
Array Length
- 如果對象是個數(shù)組的話曲尸,對象頭中就必須要有一塊用于記錄數(shù)組長度的數(shù)據(jù),不然虛擬機無法確定數(shù)組的大小男翰。32位的JVM上另患,長度為32位;64位JVM則為64位蛾绎。64位JVM如果開啟
+UseCompressedOops
選項(開啟指針壓縮)潭袱,該區(qū)域長度也將由64位壓縮至32位。32位HotSpot VM是不支持UseCompressedOops參數(shù)的吨些,只有64位HotSpot VM才支持痢畜。
實例數(shù)據(jù)
- 實例數(shù)據(jù)是對象真正存儲的有效信息,也是在程序代碼中所定義的各種類型的字段內(nèi)容肺稀。無論是從父類繼承下來的第股,還是自己本身定義的,都要記錄下來话原。
- 這部分的存儲順序會受到虛擬機分配策略參數(shù)和字段在Java源碼中定義順序的影響夕吻。
- HotSpot虛擬機默認(rèn)的分配策略為longs/doubles、ints繁仁、shorts/chars涉馅、bytes/booleans、oops(Ordinary Object Pointers)黄虱,從分配策略中可以看出稚矿,相同寬度的字段總是被分配到一起。如果 CompactFields參數(shù)值為true(默認(rèn)為true)捻浦,那子類之中占空間較小的變量也可能會插入到父類變量的空隙之中(JVM按8位對齊)晤揣。
對齊填充
- 對齊填充并不是必然存在的,也沒有特別的含義朱灿,它僅僅起著占位符的作用昧识。
- HotSpot VM的自動內(nèi)存管理系統(tǒng)要求對象起始地址必須是8字節(jié)的整數(shù)倍,換句話說就是對象的大小必須是8字節(jié)的整數(shù)倍盗扒。因此跪楞,對于普通對象來說,其對象頭的所占的字節(jié)數(shù)是8的整數(shù)倍(32bit下是1倍侣灶,64bit下是2倍)甸祭。此時則出現(xiàn)在實例數(shù)據(jù)部分可能沒有對齊,就需要對齊填充來補全褥影。假如是數(shù)組對象池户,由于對象頭多了Array Length域,因此可能對象頭也需要對齊填充。
CompressedOops(指針壓縮)
通常64位JVM消耗的內(nèi)存會比32位的最多會多用1.5倍煞檩,這是因為對象指針在64位架構(gòu)下处嫌,對象指針長度會翻倍。 對于那些將要從32位平臺移植到64位的應(yīng)用來說斟湃,平白無辜多了1/2的內(nèi)存占用熏迹,這是開發(fā)者不愿意看到的。從JDK 1.6 update14開始凝赛,64 bit JVM正式支持了 -XX:+UseCompressedOops 這個可以壓縮指針注暗,起到節(jié)約內(nèi)存占用的新參數(shù)。
-
OOP = “ordinary object pointer” 普通對象指針墓猎。 啟用CompressOops后捆昏,會壓縮的對象:
每個Class的屬性指針(靜態(tài)成員變量)
每個對象的屬性指針
普通對象數(shù)組的每個元素指針
壓縮也不是萬能的,針對一些特殊類型的指針毙沾,JVM是不會優(yōu)化的骗卜。 比如指向PermGen的Class對象指針,本地變量左胞,堆棧元素寇仓,入?yún)ⅲ祷刂悼局妫琋ULL指針不會被壓縮遍烦。
原理:32位內(nèi)最多可以表示4GB,64位地址分為堆的基地址+偏移量躺枕,當(dāng)堆內(nèi)存<32GB時候服猪,在壓縮過程中,把偏移量/8后保存到32位地址拐云。在解壓再把32位地址放大8倍罢猪,所以啟用CompressedOops的條件是堆內(nèi)存要在4GB*8=32GB以內(nèi)。
零基壓縮是針對壓解壓動作的進一步優(yōu)化叉瘩。它通過改變正常指針的隨機地址分配特性坡脐,強制從零開始做分配(需要OS支持),進一步提高了壓解壓效率房揭。要啟用零基壓縮,你分配給JVM的內(nèi)存大小必須控制在4G以上晌端,32G以下捅暴。