1.场景:匿名偏向
2.场景:重入
重入后的状态
3.场景:重新加锁
重新加锁(再次偏向)是指线程t1 释放偏向锁后,再次访问这个偏向锁。
第二次偏向的性能要比第一次高,因为第一次还需要有一个CAS设置线程id的过程。
bytecodeInterpreter.cpp
/* monitorenter and monitorexit for locking/unlocking an object */
CASE(_monitorenter): {
//为这个锁 创建一个空的Lock Record
oop lockee = STACK_OBJECT(-1);
CHECK_NULL(lockee);
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) {
//把当前锁对象的markword存储到1ock Record当中的displaced header
entry->set_obj(lockee);
int success = false;
uintptr_t epoch_mask_in_place = (uintptr_t)markOopDesc::epoch_mask_in_place;
markOop mark = lockee->mark();
intptr_t hash = (intptr_t) markOopDesc::no_hash;
// 是否开启偏向锁
if (mark->has_bias_pattern()) {
uintptr_t thread_ident;
uintptr_t anticipated_bias_locking_value;
//获取线程id
thread_ident = (uintptr_t)istate->thread();
anticipated_bias_locking_value =
(((uintptr_t)lockee->klass()->prototype_header() | thread_ident) ^ (uintptr_t)mark) &
~((uintptr_t) markOopDesc::age_mask_in_place);
if (anticipated_bias_locking_value == 0) {
// 值为0 表示已经是偏向自己
if (PrintBiasedLockingStatistics) {
(* BiasedLocking::biased_lock_entry_count_addr())++;
}
success = true;
}
// 偏向禁用
else if ((anticipated_bias_locking_value & markOopDesc::biased_lock_mask_in_place) != 0) {
// try revoke bias 禁用:撤销偏向达到40次 从类当中拿到的对象头
markOop header = lockee->klass()->prototype_header();
if (hash != markOopDesc::no_hash) {
header = header->copy_set_hash(hash);
}
if (lockee->cas_set_mark(header, mark) == mark) {
if (PrintBiasedLockingStatistics)
(*BiasedLocking::revoked_lock_entry_count_addr())++;
}
}
//poch_mask_in_place 判断是否过期
else if ((anticipated_bias_locking_value & epoch_mask_in_place) !=0) {
// try rebias 重新偏向
// 创建一个偏向当前线程的markword
markOop new_header = (markOop) ( (intptr_t) lockee->klass()->prototype_header() | thread_ident);
if (hash != markOopDesc::no_hash) {
new_header = new_header->copy_set_hash(hash);
}
//将该锁设置为偏向当前线程
if (lockee->cas_set_mark(new_header, mark) == mark) {
if (PrintBiasedLockingStatistics)
(* BiasedLocking::rebiased_lock_entry_count_addr())++;
}
else {
//CAS失败
CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
}
success = true;
}
else {
// 匿名偏向 或者 偏向了别人 这两种情况
// try to bias towards thread in case object is anonymously biased
// 在内存中 创建一个匿名可偏向 header 101
markOop header = (markOop) ((uintptr_t) mark & ((uintptr_t)markOopDesc::biased_lock_mask_in_place |
(uintptr_t)markOopDesc::age_mask_in_place |
epoch_mask_in_place));
if (hash != markOopDesc::no_hash) {
header = header->copy_set_hash(hash);
}
// 又创建一个偏向自己的new_header thread_id + 101
markOop new_header = (markOop) ((uintptr_t) header | thread_ident);
// debugging hint
DEBUG_ONLY(entry->lock()->set_displaced_header((markOop) (uintptr_t) 0xdeaddead);)
// CAS 如果是匿名偏向,就改为偏向自己,将new_header 设置到锁的markword当中去
if (lockee->cas_set_mark(new_header, header) == header) {
if (PrintBiasedLockingStatistics)
(* BiasedLocking::anonymously_biased_lock_entry_count_addr())++;
}
else {
//CAS失败
CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
}
success = true;
}
}
// 当前偏向的线程不是自己
// traditional lightweight locking
if (!success) {
//首先产生一个无锁的markword
markOop displaced = lockee->mark()->set_unlocked();
entry->lock()->set_displaced_header(displaced);
bool call_vm = UseHeavyMonitors;
// CAS将这个无锁的markword替换到锁记录中,如果成功则轻量级锁加锁成功
if (call_vm || lockee->cas_set_mark((markOop)entry, displaced) != displaced) {
//进入这个if表示轻量级锁加锁失败
//是否是重入的场景
if (!call_vm && THREAD->is_lock_owned((address) displaced->clear_lock_bits())) {
entry->lock()->set_displaced_header(NULL);
} else {
//重量级锁
CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
}
}
}
//执行CPU的下一条指令
UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);
} else {
istate->set_msg(more_monitors);
UPDATE_PC_AND_RETURN(0); // Re-execute
}
}
CASE(_monitorexit): {
oop lockee = STACK_OBJECT(-1);
CHECK_NULL(lockee);
// derefing's lockee ought to provoke implicit null check
// find our monitor slot
BasicObjectLock* limit = istate->monitor_base();
BasicObjectLock* most_recent = (BasicObjectLock*) istate->stack_base();
while (most_recent != limit ) {
if ((most_recent)->obj() == lockee) {
BasicLock* lock = most_recent->lock();
markOop header = lock->displaced_header();
most_recent->set_obj(NULL);
if (!lockee->mark()->has_bias_pattern()) {
bool call_vm = UseHeavyMonitors;
// If it isn't recursive we either must swap old header or call the runtime
if (header != NULL || call_vm) {
markOop old_header = markOopDesc::encode(lock);
if (call_vm || lockee->cas_set_mark(header, old_header) != old_header) {
// restore object for the slow case
most_recent->set_obj(lockee);
CALL_VM(InterpreterRuntime::monitorexit(THREAD, most_recent), handle_exception);
}
}
}
UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);
}
most_recent++;
}
// Need to throw illegal monitor state exception
CALL_VM(InterpreterRuntime::throw_illegal_monitor_state_exception(THREAD), handle_exception);
ShouldNotReachHere();
}
批量重偏向
偏向撤销
批量重偏向-轻量级锁降级为偏向锁
当一个线程创建了大量对象并执行了初始的同步操作,后来另一个线程也来将这些对象作为锁对象进行操作,会导偏向锁重偏向的操作。JVM 认为如果对同一个类的对象进行了20次偏向撤销,就会产生批量重偏向。
此时锁 由 轻量级锁 =》偏向锁。
批量撤销
批量撤销-禁用偏向锁
JVM 认为如果对同一个类的对象进行了40次单个撤销,JVM会认为代码设计有问题,对所有该类的对象进行批量撤销,并且从此对这个类禁用偏向锁,修改类class文件中的markword为001。
在超过40次本应该为可偏向状态偏向锁101的新对象,在经历过批量重偏向和批量撤销后直接在实例化后转为无锁001。
- 批量重偏向和批量撤销是针对类的优化,和对象无关。
- 偏向锁重偏向一次之后不可再次重偏向。
- 当某个类已经触发批量撤销机制后,JVM会默认当前类产生了严重的问题,剥夺了该类的新实例对象使用偏向锁的权利
偏向过期
当一个类被加载到元空间后,这个类的类信息会成为创建这个类的对象的模板,这个epoch的值就是存在于类信息中的模板值,默认为01。
当发生批量重偏向时,就会对元空间中这个类的epoch值 +1。
当一个线程访问偏向锁,发现epoch值不等于初始值时,就认为这个偏向锁过期了,可以直接把该锁设置为偏向自己。