staticinlineboolis_MP() { // During bootstrap if _processor_count is not yet initialized // we claim to be MP as that is safest. If any platform has a // stub generator that might be triggered in this phase and for // which being declared MP when in fact not, is a problem - then // the bootstrap routine for the stub generator needs to check // the processor count directly and leave the bootstrap routine // in place until called after initialization has ocurred. return (_processor_count != 1) || AssumeMP; }
com.mashibing.insidesync.T01_Sync1$Lock object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 04 (object header) 05000000 (00000101000000000000000000000000) (5) 44 (object header) 00000000 (00000000000000000000000000000000) (0) 84 (object header) 49 ce 0020 (01001001110011100000000000100000) (536923721) 124 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4bytesexternal=4 bytes total
1 2 3 4 5 6 7 8
com.mashibing.insidesync.T02_Sync2$Lock object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 04 (object header) 0590 2e 1e (00000101100100000010111000011110) (506368005) 44 (object header) 1b 020000 (00011011000000100000000000000000) (539) 84 (object header) 49 ce 0020 (01001001110011100000000000100000) (536923721) 124 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4bytesexternal=4 bytes tota
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); }
升级重量级锁:-> 向操作系统申请资源,linux mutex , CPU 从 3 级-0 级系统调用,线程挂起,进入等待队列,等待操作系统的调度,然后再映射回用户空间
(以上实验环境是 JDK11,打开就是偏向锁,而 JDK8 默认对象头是无锁)
偏向锁默认是打开的,但是有一个时延,如果要观察到偏向锁,应该设定参数
如果计算过对象的 hashCode,则对象无法进入偏向状态!
轻量级锁重量级锁的 hashCode 存在与什么地方?
答案:线程栈中,轻量级锁的 LR 中,或是代表重量级锁的 ObjectMonitor 的成员中
关于 epoch: (不重要)
批量重偏向与批量撤销渊源:从偏向锁的加锁解锁过程中可看出,当只有一个线程反复进入同步块时,偏向锁带来的性能开销基本可以忽略,但是当有其他线程尝试获得锁时,就需要等到 safe point 时,再将偏向锁撤销为无锁状态或升级为轻量级,会消耗一定的性能,所以在多线程竞争频繁的情况下,偏向锁不仅不能提高性能,还会导致性能下降。于是,就有了批量重偏向与批量撤销的机制。
原理以 class 为单位,为每个 class 维护解决场景批量重偏向(bulk rebias)机制是为了解决:一个线程创建了大量对象并执行了初始的同步操作,后来另一个线程也来将这些对象作为锁对象进行操作,这样会导致大量的偏向锁撤销操作。批量撤销(bulk revoke)机制是为了解决:在明显多线程竞争剧烈的场景下使用偏向锁是不合适的。
一个偏向锁撤销计数器,每一次该 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 的锁,直接走轻量级锁的逻辑。