虛擬機中對象頭部信息多線程
/*hotspot/src/share/vm/oops/oop.hpp*/ class oopDesc { friend class VMStructs; private: volatile markOop _mark; union _metadata { Klass* _klass; narrowKlass _compressed_klass; } _metadata;
能夠看見對象頭中結構oop
32位的對象頭結構,64位結構略性能
// 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)
biased_lock 表示是否偏向鎖spa
lock類型線程
代碼片斷指針
// 默認嘗試偏向鎖 void ObjectSynchronizer::fast_enter(Handle obj, BasicLock* lock, bool attempt_rebias, TRAPS) { //是否使用偏向鎖 if (UseBiasedLocking) { //未到達safepoint,嘗試重偏向 if (!SafepointSynchronize::is_at_safepoint()) { BiasedLocking::Condition cond = BiasedLocking::revoke_and_rebias(obj, attempt_rebias, THREAD); if (cond == BiasedLocking::BIAS_REVOKED_AND_REBIASED) { return; } } else { //在safepoint進行撤銷偏向鎖 assert(!attempt_rebias, "can not rebias toward VM thread"); BiasedLocking::revoke_at_safepoint(obj); } //若是走到這裏則說明偏向鎖已撤銷,進行slow_enter(加輕量級鎖) assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now"); } slow_enter (obj, lock, THREAD) ; } // 輕量級鎖 void ObjectSynchronizer::slow_enter(Handle obj, BasicLock* lock, TRAPS) { markOop mark = obj->mark(); assert(!mark->has_bias_pattern(), "should not see bias pattern here"); if (mark->is_neutral()) { //若是無鎖狀態 // 在lock對象上設置displaced mark word lock->set_displaced_header(mark); //使用CAS操做交換lock和object的mark word if (mark == (markOop) Atomic::cmpxchg_ptr(lock, obj()->mark_addr(), mark)) { TEVENT (slow_enter: release stacklock) ; return ; } // 若是CAS失敗,則跳到下面inflate(重量級鎖) } else //若是給相同對象加鎖,則後續的鎖的displaced mark設置爲NULL(不會重複上鎖) if (mark->has_locker() && THREAD->is_lock_owned((address)mark->locker())) { assert(lock != mark->locker(), "must not re-lock the same lock"); assert(lock != (BasicLock*)obj->mark(), "don't relock with same BasicLock"); lock->set_displaced_header(NULL); return; } //標記lock對象爲unused,後續由CMS回收,並調用inflate(重量級鎖) lock->set_displaced_header(markOopDesc::unused_mark()); ObjectSynchronizer::inflate(THREAD, obj())->enter(THREAD); }
在大多數狀況下,線程之間不存在競爭關係,即一個鎖會被某個線程屢次使用。若是每次都須要申請鎖,開銷會比較大。所以出現了偏向鎖,獲取偏向鎖以後,若是是不存在其餘線程競爭鎖,那麼就不須要調用CAS來獲取鎖,以達到減小I/O的目的code
case 1:當該對象第一次被線程得到鎖的時候,發現是匿名偏向狀態,則會用CAS指令,將mark word中的thread id由0改爲當前線程Id。若是成功,則表明得到了偏向鎖,繼續執行同步塊中的代碼。不然,將偏向鎖撤銷,升級爲輕量級鎖。orm
case 2:當被偏向的線程再次進入同步塊時,發現鎖對象偏向的就是當前線程,在經過一些額外的檢查後(細節見後面的文章),會往當前線程的棧中添加一條Displaced Mark Word爲空的Lock Record中,而後繼續執行同步塊的代碼,由於操縱的是線程私有的棧,所以不須要用到CAS指令;因而可知偏向鎖模式下,當被偏向的線程再次嘗試得到鎖時,僅僅進行幾個簡單的操做就能夠了,在這種狀況下,synchronized關鍵字帶來的性能開銷基本能夠忽略。對象
case 3.當其餘線程進入同步塊時,發現已經有偏向的線程了,則會進入到撤銷偏向鎖的邏輯裏,通常來講,會在safepoint中去查看偏向的線程是否還存活,若是存活且還在同步塊中則將鎖升級爲輕量級鎖,原偏向的線程繼續擁有鎖,當前線程則走入到鎖升級的邏輯裏;若是偏向的線程已經不存活或者不在同步塊中,則將對象頭的mark word改成無鎖狀態(unlocked),以後再升級爲輕量級鎖。隊列
因而可知,偏向鎖升級的時機爲:當鎖已經發生偏向後,只要有另外一個線程嘗試得到偏向鎖,則該偏向鎖就會升級成輕量級鎖。固然這個說法不絕對,由於還有批量重偏向這一機制。
當有其餘線程嘗試得到鎖時,是根據遍歷偏向線程的lock record來肯定該線程是否還在執行同步塊中的代碼。所以偏向鎖的解鎖很簡單,僅僅將棧中的最近一條lock record的obj字段設置爲null。須要注意的是,偏向鎖的解鎖步驟中並不會修改對象頭中的thread id。
從上文偏向鎖的加鎖解鎖過程當中能夠看出,當只有一個線程反覆進入同步塊時,偏向鎖帶來的性能開銷基本能夠忽略,可是當有其餘線程嘗試得到鎖時,就須要等到safe point時將偏向鎖撤銷爲無鎖狀態或升級爲輕量級/重量級鎖。safe point這個詞咱們在GC中常常會提到,其表明了一個狀態,在該狀態下全部線程都是暫停的(大概這麼個意思),詳細能夠看這篇文章。總之,偏向鎖的撤銷是有必定成本的,若是說運行時的場景自己存在多線程競爭的,那偏向鎖的存在不只不能提升性能,並且會致使性能降低。所以,JVM中增長了一種批量重偏向/撤銷的機制。
存在以下兩種狀況:(見官方論文第4小節):
1.一個線程建立了大量對象並執行了初始的同步操做,以後在另外一個線程中將這些對象做爲鎖進行以後的操做。這種case下,會致使大量的偏向鎖撤銷操做。
2.存在明顯多線程競爭的場景下使用偏向鎖是不合適的,例如生產者/消費者隊列。
批量重偏向(bulk rebias)機制是爲了解決第一種場景。批量撤銷(bulk revoke)則是爲了解決第二種場景。
其作法是:以class爲單位,爲每一個class維護一個偏向鎖撤銷計數器,每一次該class的對象發生偏向撤銷操做時,該計數器+1,當這個值達到重偏向閾值(默認20)時,JVM就認爲該class的偏向鎖有問題,所以會進行批量重偏向。每一個class對象會有一個對應的epoch字段,每一個處於偏向鎖狀態對象的mark word中也有該字段,其初始值爲建立該對象時,class中的epoch的值。每次發生批量重偏向時,就將該值+1,同時遍歷JVM中全部線程的棧,找到該class全部正處於加鎖狀態的偏向鎖,將其epoch字段改成新值。下次得到鎖時,發現當前對象的epoch值和class的epoch不相等,那就算當前已經偏向了其餘線程,也不會執行撤銷操做,而是直接經過CAS操做將其mark word的Thread Id 改爲當前線程Id。
當達到重偏向閾值後,假設該class計數器繼續增加,當其達到批量撤銷的閾值後(默認40),JVM就認爲該class的使用場景存在多線程競爭,會標記該class爲不可偏向,以後,對於該class的鎖,直接走輕量級鎖的流程
product(intx, BiasedLockingBulkRebiasThreshold, 20, "Threshold of number of revocations per type to try to " "rebias all objects in the heap of that type") product(intx, BiasedLockingBulkRevokeThreshold, 40, "Threshold of number of revocations per type to permanently " "revoke biases of all objects in the heap of that type")
加鎖過程代碼
CASE(_monitorenter): { oop lockee = STACK_OBJECT(-1); // 建立一個空堆對象lockee CHECK_NULL(lockee); // 遍歷stack中的lock對象,尋找是否存在指向對象爲待加鎖對象的 BasicObjectLock* limit = istate->monitor_base(); BasicObjectLock* most_recent = (BasicObjectLock*) istate->stack_base(); BasicObjectLock* entry = NULL; while (most_recent != limit ) { if (most_recent->obj() == NULL) entry = most_recent; else if (most_recent->obj() == lockee) break; most_recent++; } if (entry != NULL) { //已存在鎖對象,構建一個無鎖狀態的Displaced Mark Word //設置到Lock Record中去 entry->set_obj(lockee); markOop displaced = lockee->mark()->set_unlocked(); entry->lock()->set_displaced_header(displaced); if (Atomic::cmpxchg_ptr(entry, lockee->mark_addr(), displaced) != displaced) { // 若是CAS替換不成功,表明鎖對象不是無鎖狀態,這時候判斷下是否是鎖重入 if (THREAD->is_lock_owned((address) displaced->clear_lock_bits())) { entry->lock()->set_displaced_header(NULL); } else { // CAS操做失敗則調用monitorenter CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception); } } UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1); } else { istate->set_msg(more_monitors); UPDATE_PC_AND_RETURN(0); // Re-execute } }
解鎖過程代碼與加鎖基本相同,省略