一个偏向锁撤销计数器,每一次该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的锁,直接走轻量级锁的逻辑。
voidObjectSynchronizer::fast_enter(Handle obj, BasicLock* lock, bool attempt_rebias, TRAPS){ if (UseBiasedLocking) { 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 { assert(!attempt_rebias, "can not rebias toward VM thread"); BiasedLocking::revoke_at_safepoint(obj); } assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now"); }
slow_enter (obj, lock, THREAD) ; }
voidObjectSynchronizer::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()) { // Anticipate successful CAS -- the ST of the displaced mark must // be visible <= the ST performed by the CAS. lock->set_displaced_header(mark); if (mark == (markOop) Atomic::cmpxchg_ptr(lock, obj()->mark_addr(), mark)) { TEVENT (slow_enter: release stacklock) ; return ; } // Fall through to inflate() ... } else 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; }
#if 0 // The following optimization isn't particularly useful. if (mark->has_monitor() && mark->monitor()->is_entered(THREAD)) { lock->set_displaced_header (NULL) ; return ; } #endif
// The object header will never be displaced to this lock, // so it does not matter what the value is, except that it // must be non-zero to avoid looking like a re-entrant lock, // and must not look locked either. lock->set_displaced_header(markOopDesc::unused_mark()); ObjectSynchronizer::inflate(THREAD, obj())->enter(THREAD); }
//%note monitor_1 IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorexit(JavaThread* thread, BasicObjectLock* elem)) #ifdef ASSERT thread->last_frame().interpreter_frame_verify_monitor(elem); #endif Handle h_obj(thread, elem->obj()); assert(Universe::heap()->is_in_reserved_or_null(h_obj()), "must be NULL or an object"); if (elem == NULL || h_obj()->is_unlocked()) { THROW(vmSymbols::java_lang_IllegalMonitorStateException()); } ObjectSynchronizer::slow_exit(h_obj(), elem->lock(), thread); // Free entry. This must be done here, since a pending exception might be installed on // exit. If it is not cleared, the exception handling code will try to unlock the monitor again. elem->set_obj(NULL); #ifdef ASSERT thread->last_frame().interpreter_frame_verify_monitor(elem); #endif IRT_END
// lock method if synchronized if (METHOD->is_synchronized()) { // oop rcvr = locals[0].j.r; oop rcvr; if (METHOD->is_static()) { rcvr = METHOD->constants()->pool_holder()->java_mirror(); } else { rcvr = LOCALS_OBJECT(0); VERIFY_OOP(rcvr); } // The initial monitor is ours for the taking BasicObjectLock* mon = &istate->monitor_base()[-1]; oop monobj = mon->obj(); assert(mon->obj() == rcvr, "method monitor mis-initialized");
bool success = UseBiasedLocking; if (UseBiasedLocking) { markOop mark = rcvr->mark(); if (mark->has_bias_pattern()) { // The bias pattern is present in the object's header. Need to check // whether the bias owner and the epoch are both still current. intptr_t xx = ((intptr_t) THREAD) ^ (intptr_t) mark; xx = (intptr_t) rcvr->klass()->prototype_header() ^ xx; intptr_t yy = (xx & ~((int) markOopDesc::age_mask_in_place)); if (yy != 0 ) { // At this point we know that the header has the bias pattern and // that we are not the bias owner in the current epoch. We need to // figure out more details about the state of the header in order to // know what operations can be legally performed on the object's // header.
// If the low three bits in the xor result aren't clear, that means // the prototype header is no longer biased and we have to revoke // the bias on this object.
if (yy & markOopDesc::biased_lock_mask_in_place == 0 ) { // Biasing is still enabled for this data type. See whether the // epoch of the current bias is still valid, meaning that the epoch // bits of the mark word are equal to the epoch bits of the // prototype header. (Note that the prototype header's epoch bits // only change at a safepoint.) If not, attempt to rebias the object // toward the current thread. Note that we must be absolutely sure // that the current epoch is invalid in order to do this because // otherwise the manipulations it performs on the mark word are // illegal. if (yy & markOopDesc::epoch_mask_in_place == 0) { // The epoch of the current bias is still valid but we know nothing // about the owner; it might be set or it might be clear. Try to // acquire the bias of the object using an atomic operation. If this // fails we will go in to the runtime to revoke the object's bias. // Note that we first construct the presumed unbiased header so we // don't accidentally blow away another thread's valid bias. intptr_t unbiased = (intptr_t) mark & (markOopDesc::biased_lock_mask_in_place | markOopDesc::age_mask_in_place | markOopDesc::epoch_mask_in_place); if (Atomic::cmpxchg_ptr((intptr_t)THREAD | unbiased, (intptr_t*) rcvr->mark_addr(), unbiased) != unbiased) { CALL_VM(InterpreterRuntime::monitorenter(THREAD, mon), handle_exception); } } else { try_rebias: // At this point we know the epoch has expired, meaning that the // current "bias owner", if any, is actually invalid. Under these // circumstances _only_, we are allowed to use the current header's // value as the comparison value when doing the cas to acquire the // bias in the current epoch. In other words, we allow transfer of // the bias from one thread to another directly in this situation. xx = (intptr_t) rcvr->klass()->prototype_header() | (intptr_t) THREAD; if (Atomic::cmpxchg_ptr((intptr_t)THREAD | (intptr_t) rcvr->klass()->prototype_header(), (intptr_t*) rcvr->mark_addr(), (intptr_t) mark) != (intptr_t) mark) { CALL_VM(InterpreterRuntime::monitorenter(THREAD, mon), handle_exception); } } } else { try_revoke_bias: // The prototype mark in the klass doesn't have the bias bit set any // more, indicating that objects of this data type are not supposed // to be biased any more. We are going to try to reset the mark of // this object to the prototype value and fall through to the // CAS-based locking scheme. Note that if our CAS fails, it means // that another thread raced us for the privilege of revoking the // bias of this particular object, so it's okay to continue in the // normal locking code. // xx = (intptr_t) rcvr->klass()->prototype_header() | (intptr_t) THREAD; if (Atomic::cmpxchg_ptr(rcvr->klass()->prototype_header(), (intptr_t*) rcvr->mark_addr(), mark) == mark) { // (*counters->revoked_lock_entry_count_addr())++; success = false; } } } } else { cas_label: success = false; } } if (!success) { markOop displaced = rcvr->mark()->set_unlocked(); mon->lock()->set_displaced_header(displaced); if (Atomic::cmpxchg_ptr(mon, rcvr->mark_addr(), displaced) != displaced) { // Is it simple recursive case? if (THREAD->is_lock_owned((address) displaced->clear_lock_bits())) { mon->lock()->set_displaced_header(NULL); } else { CALL_VM(InterpreterRuntime::monitorenter(THREAD, mon), handle_exception); } } } } THREAD->clr_do_not_unlock();