環境: 本機爲64位操做系統。jdk 1.8數組
1、對象在內存中存儲的佈局能夠分爲3塊區域:對象頭(Header),實例數據(Instance Data),對齊填充(Padding)。ruby
2、咱們能夠經過maven工程引用對應的 jol-core jar包。將對應的內存分佈打印出來作相應的研究。架構
我這裏寫勒一個類:User,4個字段 佔用 4*4 byte =16 byte 共16字節 (實例數據佔用16字節)併發
ObjectHeader類的打印對象的總體分佈:主要代碼以下:jvm
System.out.println(ClassLayout.parseInstance(user).instanceSize());maven
System.out.println(ClassLayout.parseInstance(user).toPrintable());ide
產生結果:oop
上圖中:能夠看到,對象在內存中分爲3塊區域。 對齊填充 在JVM中定義爲 8byte 的 整數倍。 因此 28 % 8 =4 因此須要padding 4 byte 。佈局
Header包含以下內容:主要由 Mark World (8byte)+ Class Pointer (4byte) (類型指針)優化
1. Mark World 根據 對象 lock 標記的狀態不一樣 , 所存儲的內容也會發生改變. 具體以下圖.
|------------------------------------------------------------------------------|--------------------| | Mark Word (64 bits) | State | |------------------------------------------------------------------------------|--------------------| | unused:25 | identity_hashcode:31 | unused:1 | age:4 | biased_lock:1 | lock:2 | Normal | |------------------------------------------------------------------------------|--------------------| | thread:54 | epoch:2 | unused:1 | age:4 | biased_lock:1 | lock:2 | Biased | |------------------------------------------------------------------------------|--------------------| | ptr_to_lock_record:62 | lock:2 | Lightweight Locked | |------------------------------------------------------------------------------|--------------------| | ptr_to_heavyweight_monitor:62 | lock:2 | Heavyweight Locked | |------------------------------------------------------------------------------|--------------------| | | lock:2 | Marked for GC | |------------------------------------------------------------------------------|--------------------|
biased_lock | lock | 狀態 |
---|---|---|
0 | 01 | 無鎖 |
1 | 01 | 偏向鎖 |
0 | 00 | 輕量級鎖 |
0 | 10 | 重量級鎖 |
0 | 11 | GC標記 |
lock:2位的鎖狀態標記位,因爲但願用盡量少的二進制位表示儘量多的信息,因此設置了lock標記。該標記的值不一樣,整個mark word表示的含義不一樣。 biased_lock:對象是否啓用偏向鎖標記,只佔1個二進制位。爲1時表示對象啓用偏向鎖,爲0時表示對象沒有偏向鎖。
age:4位的Java對象年齡。在GC中,若是對象在Survivor區複製一次,年齡增長1。當對象達到設定的閾值時,將會晉升到老年代。默認狀況下,並行GC的年齡閾值爲15,併發GC的年齡閾值爲6。因爲age只有4位,因此最大值爲15,這就是-XX:MaxTenuringThreshold
選項最大值爲15的緣由。
identity_hashcode:25位的對象標識Hash碼,採用延遲加載技術。調用方法System.identityHashCode()
計算,並會將結果寫到該對象頭中。當對象被鎖定時,該值會移動到管程Monitor中。
thread:持有偏向鎖的線程ID。
epoch:偏向時間戳。
ptr_to_lock_record:指向棧中鎖記錄的指針。
ptr_to_heavyweight_monitor:指向管程Monitor的指針。
2. Class Pointer (類型指針) 這一部分用於存儲對象的類型指針,該指針指向它的類元數據,JVM經過這個指針肯定對象是哪一個類的實例。該指針的位長度爲JVM的一個字大小,即32位的JVM爲32位,64位的JVM爲64位。
若是應用的對象過多,使用64位的指針將浪費大量內存,統計而言,64位的JVM將會比32位的JVM多耗費50%的內存。爲了節約內存能夠使用選項+UseCompressedOops
開啓指針壓縮,其中,oop即ordinary object pointer普通對象指針。開啓該選項後,下列指針將壓縮至32位:
- 每一個Class的屬性指針(即靜態變量)
- 每一個對象的屬性指針(即對象變量)
- 普通對象數組的每一個元素指針
上例證:
禁用指針壓縮: -XX:-UseCompressedOops
開啓指針壓縮,jvm默認開啓
固然,也不是全部的指針都會壓縮,一些特殊類型的指針JVM不會優化,好比指向PermGen的Class對象指針(JDK8中指向元空間的Class對象指針)、本地變量、堆棧元素、入參、返回值和NULL指針等。
3. 若是對象是一個數組,那麼對象頭還須要有額外的空間用於存儲數組的長度,這部分數據的長度也隨着JVM架構的不一樣而不一樣:32位的JVM上,長度爲32位;64位JVM則爲64位。64位JVM若是開啓+UseCompressedOops
選項,該區域長度也將由64位壓縮至32位。