概覽
-
對象頭
存放:關於堆對象的佈局、類型、GC狀態、同步狀態和標識哈希碼的基本信息。Java對象和vm內部對象都有一個共同的對象頭格式。
(後面做詳細介紹) -
實例數據
存放:類的數據信息,父類的信息,對象字段屬性信息。
如果對象有屬性字段,則這裏會有數據信息。如果對象無屬性字段,則這裏就不會有數據。
根據字段類型的不同佔不同的字節,例如boolean類型佔1個字節,int類型佔4個字節等等; -
對齊填充
存放:爲了字節對齊,填充的數據,不是必須的。
默認情況下,Java虛擬機堆中對象的起始地址需要對齊至8的倍數。
假如對象頭大小爲12,實例數據大小爲5,最近且大於12+5的8的倍數值是24,則對齊補充大小爲:24-12-5=7。
爲什麼需要對象填充?
字段內存對齊的其中一個原因,是讓字段只出現在同一CPU的緩存行中。如果字段不是對齊的,那麼就有可能出現跨緩存行的字段。也就是說,該字段的讀取可能需要替換兩個緩存行,而該字段的存儲也會同時污染兩個緩存行。這兩種情況對程序的執行效率而言都是不利的。其實對其填充的最終目的是爲了計算機高效尋址。
對象頭
mark word
OpenJDK(JDK8)地址:https://github.com/openjdk/jdk
根據OpenJDK 官方源碼中MarkOop.hpp文件中給的註釋介紹,可以大概看出mark word的組成。
MarkOop.hpp中的註釋1:
32 bits:
--------
hash:25 ------------>| age:4 biased_lock:1 lock:2 (normal object)
JavaThread*:23 epoch:2 age:4 biased_lock:1 lock:2 (biased object)
size:32 ------------------------------------------>| (CMS free block)
PromotedObject*:29 ---------->| promo_bits:3 ----->| (CMS promoted object)
64 bits:
--------
unused:25 hash:31 -->| unused:1 age:4 biased_lock:1 lock:2 (normal object)
JavaThread*:54 epoch:2 unused:1 age:4 biased_lock:1 lock:2 (biased object)
PromotedObject*:61 --------------------->| promo_bits:3 ----->| (CMS promoted object)
size:64 ----------------------------------------------------->| (CMS free block)
MarkOop.hpp中的註釋2:
[JavaThread* | epoch | age | 1 | 01] lock is biased toward given thread
[0 | epoch | age | 1 | 01] lock is anonymously biased
- the two lock bits are used to describe three states: locked/unlocked and monitor.
[ptr | 00] locked ptr points to real header on stack
[header | 0 | 01] unlocked regular object header
[ptr | 10] monitor inflated lock (header is wapped out)
[ptr | 11] marked used by markSweep to mark an object
not valid at any other time
MarkOop.hpp中的源碼1:
enum { age_bits = 4,
lock_bits = 2,
biased_lock_bits = 1,
max_hash_bits = BitsPerWord - age_bits - lock_bits - biased_lock_bits,
hash_bits = max_hash_bits > 31 ? 31 : max_hash_bits,
cms_bits = LP64_ONLY(1) NOT_LP64(0),
epoch_bits = 2
};
如圖:
MarkOop.hpp中的源碼2:
enum { locked_value = 0,
unlocked_value = 1,
monitor_value = 2,
marked_value = 3,
biased_lock_pattern = 5
};
- locked_value
輕量級鎖狀態值,mark word 最後2位爲00,轉爲10進製爲0。 - unlocked_value
無鎖狀態值,mark word 最後3位爲001,轉爲10進製爲1。 - monitor_value
重量級鎖狀態值,mark word 最後2位爲10,轉爲10進製爲2。 - marked_value
mark word 最後2位爲11,轉爲10進製爲3。
作用比較複雜,
1:當鎖升級爲重量級鎖的過程中,會將markword設置爲這個值。
2:當對象GC時也要使用這個值。
markOop.hpp部分源碼如下:
// 僅用於存儲到Lock Record中,用來表示鎖正在使用重量級監視器(輕量級鎖膨脹爲重量級鎖之前會這麼做)
static markOop unused_mark() {
return (markOop) marked_value;
}
// age operations
markOop set_marked() { return markOop((value() & ~lock_mask_in_place) | marked_value); }
markOop set_unmarked() { return markOop((value() & ~lock_mask_in_place) | unlocked_value); }
- biased_lock_pattern
偏向鎖狀態值,mark word 最後3位爲101,轉爲10進製爲5。
markOop.cpp中還有以下代碼,用以判斷當前markword處於哪種鎖狀態:
// 輕量級鎖
bool is_locked() const {
return (mask_bits(value(), lock_mask_in_place) != unlocked_value);
}
// 偏向鎖
bool is_unlocked() const {
return (mask_bits(value(), biased_lock_mask_in_place) == unlocked_value);
}
// marked
bool is_marked() const {
return (mask_bits(value(), lock_mask_in_place) == marked_value);
}
// 無鎖
bool is_neutral() const { return (mask_bits(value(), biased_lock_mask_in_place) == unlocked_value); }
// 膨脹時 markOop 的特殊臨時狀態。 在鎖外查看標記的代碼需要考慮到這一點。
bool is_being_inflated() const { return (value() == 0); }
// 鎖對象處於升級爲重量級鎖的過程中
static markOop INFLATING() { return (markOop) 0; }
爲什麼對象頭分代佔4bit
因爲對象經過15次GC就會被放入老年代,而15轉化爲二進制就是1111,幹好佔4bit.
epoch的作用
抄自於:http://www.itqiankun.com/article/bias-lock-epoch-effect
其本質是一個時間戳,代表了偏向鎖的有效性,epoch存儲在可偏向對象的MarkWord中。
①:除了對象中的epoch,對象所屬的類class信息中,也會保存一個epoch值。
②:每當遇到一個全局安全點時(這裏的意思是說批量重偏向沒有完全替代了全局安全點,全局安全點是一直存在的),比如要對class C 進行批量再偏向,則首先對 class C中保存的epoch進行增加操作,得到一個新的epoch_new。
③:然後掃描所有持有 class C 實例的線程棧,根據線程棧的信息判斷出該線程是否鎖定了該對象,僅將epoch_new的值賦給被鎖定的對象中,也就是現在偏向鎖還在被使用的對象纔會被賦值epoch_new。
④:退出安全點後,當有線程需要嘗試獲取偏向鎖時,直接檢查 class C 中存儲的 epoch 值是否與目標對象中存儲的 epoch 值相等, 如果不相等,則說明該對象的偏向鎖已經無效了(因爲(3)步驟裏面已經說了只有偏向鎖還在被使用的對象纔會有epoch_new,這裏不相等的原因是class C裏面的epoch值是epoch_new,而當前對象的epoch裏面的值還是epoch),此時競爭線程可以嘗試對此對象重新進行偏向操作。
klass point
元數據指針class pointer,即指向方法區的instanceKlass實例(虛擬機通過這個指針來羣定這個對象是哪個類的實例)。
oop.hpp中的源碼:
class oopDesc {
friend class VMStructs;
private:
volatile markOop _mark;
union _metadata {
Klass* _klass;
narrowKlass _compressed_klass;
} _metadata;
length field
該屬性只有數組對象纔有,用以表示數組的長度。
arrayOop.hpp中有這麼一段註釋:
// The layout of array Oops is:
//
// markOop
// Klass* // 32 bits if compressed but declared 64 in LP64.
// length // shares klass memory or allocated after declared fields.
總結
可能面試的時候會被問到這個問題:爲什麼一個對象可以當成一把鎖?
這方面可以與上文中提到的對象頭、markword 進行回答即可。