概述
文章目录
- BasicObjectLock
- BasicLock
- oopDesc
- markOopDesc
- 偏向锁
- 偏向锁的获取 monitorenter
- 源码验证(bytecodeInterpreter.cpp)
基于 hotspot 源码深度解析 Synchronized 偏向锁原理。欢迎友好交流,最好附带源码作为论据。
BasicObjectLock
包装了锁对象的头部和锁对象的引用。
class BasicObjectLock {
friend class VMStructs;
private:
// 保存了被替换下来的对象的头部
BasicLock _lock; // the lock, must be double word aligned
// oop java 对象的引用
// typedef class oopDesc* oop;
oop _obj; // object holds the lock;
public:
// Manipulation
oop obj() const { return _obj; }
void set_obj(oop obj) { _obj = obj; }
BasicLock* lock() { return &_lock; }
static int size() { return sizeof(BasicObjectLock)/wordSize; }
void oops_do(OopClosure* f) { f->do_oop(&_obj); }
static int obj_offset_in_bytes() { return offset_of(BasicObjectLock, _obj); }
static int lock_offset_in_bytes() { return offset_of(BasicObjectLock, _lock); }
};
BasicLock
class BasicLock {
friend class VMStructs;
friend class JVMCIVMStructs;
private:
volatile markOop _displaced_header; // 被替换下来的对象头部
public:
markOop displaced_header() const { return _displaced_header; }
void set_displaced_header(markOop header) { _displaced_header = header; }
void print_on(outputStream* st) const;
void move_to(oop obj, BasicLock* dest);
static int displaced_header_offset_in_bytes() { return offset_of(BasicLock, _displaced_header); }
};
oopDesc
hotspot/src/share/vm/oops/oop.hpp
所有对象头部的基类
typedef class markOopDesc* markOop; // oopsHierarchy.hpp
class oopDesc {
friend class VMStructs;
private:
volatile markOop _mark; // 其实就是 markOopDesc
union _metadata { // 指向对象所属 class 对象指针
Klass* _klass;
narrowKlass _compressed_klass; // 开启指针压缩后,指向压缩后的 class 对象
} _metadata;
markOopDesc
hotspot/src/share/vm/oops/markOop.hpp
描述了标记位,其实就是带标记的 oop 对象
class markOopDesc: public oopDesc {
private:
uintptr_t value() const { return (uintptr_t) this; }
public:
enum { age_bits = 4, // 对象年龄 4bit
lock_bits = 2, // 锁标记 2bit
biased_lock_bits = 1, // 偏向锁 1bit
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
};
enum { no_hash = 0 }; // no hash value assigned
// ...
}
偏向锁
偏向锁的获取 monitorenter
- 偏向锁已经偏向当前线程:那么直接获取锁成功
- 对象类的原始头部偏向锁位发生改变,那么撤销偏向锁(将原始头部 CAS 到当前对象头部)
- 原始对象头部的 epoch 发生变化,此时表示批量偏向锁撤销,那么尝试重新获取偏向锁
- 匿名偏向锁,那么尝试获取偏向锁
- 上述失败后,那么需要进行锁升级
源码验证(bytecodeInterpreter.cpp)
CASE(_monitorenter): {
oop lockee = STACK_OBJECT(-1);
// 每个 java 的线程栈上都有一组 BasicObjectLock 对象
// 在线程栈上找到一个空闲的 BasicObjectLock 对象
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) {
// 保存锁对象,表明当前 BasicObjectLock 持有锁对象 lockee
entry->set_obj(lockee);
int success = false;
uintptr_t epoch_mask_in_place = (uintptr_t)markOopDesc::epoch_mask_in_place; // 获取 epoch(用于标识偏向锁的轮次),用于批量锁撤销。
// 获取锁对象的头部标记信息
markOop mark = lockee->mark();
// enum { no_hash = 0 };
intptr_t hash = (intptr_t) markOopDesc::no_hash;
// ====== if (mark->has_bias_pattern()) 开始 ===
// 判断是否开启了偏向锁。若开启偏向锁,锁状态位为 5
// [JavaThread* | epoch | age | 1 | 01](src/hotspot/share/oops/markOop.hpp)
if (mark->has_bias_pattern()) {
uintptr_t thread_ident;
uintptr_t anticipated_bias_locking_value;
thread_ident = (uintptr_t)istate->thread();
// 比较原始对象与当前对象除了年龄位的其他位是否一致
// lockee->klass()->prototype_header():对象的原始头部信息(类的信息),类是对象的模板,对象是类的实例
// ((uintptr_t)lockee->klass()->prototype_header() | thread_ident):将对象的原始信息组合上线程标识
// (((uintptr_t)lockee->klass()->prototype_header() | thread_ident) ^ (uintptr_t)mark):将对象的原始信息组合上线程标识的结果与当前对象头部比较,若完全相同则为 0,表示当前对象已经偏向了当前线程
// & ~((uintptr_t) markOopDesc::age_mask_in_place):避免由于年龄位不一致导致上一步结果不为 0
anticipated_bias_locking_value =
(((uintptr_t)lockee->klass()->prototype_header() | thread_ident) ^ (uintptr_t)mark) &
~((uintptr_t) markOopDesc::age_mask_in_place);
// 1. 原始对象与当前对象除了年龄位的其他位一致
// 表明还没有批量撤销偏向锁,且当前线程持有了偏向锁,直接退出
if (anticipated_bias_locking_value == 0) {
// already biased towards this thread, nothing to do
if (PrintBiasedLockingStatistics) {
(* BiasedLocking::biased_lock_entry_count_addr())++;
}
success = true; // 当前线程已经获取了偏向锁,直接退出
}
// 执行到此处说明 anticipated_bias_locking_value 不为 0
// 2. 从 anticipated_bias_locking_value 值中截断出偏向锁位,若存在差异,说明原始对象头部的偏向锁位发生改变,此时撤销对象的偏向锁(注意:一定是原始对象头部发生改变,因为 if (mark->has_bias_pattern()) 前置判断已经定义当前对象的头部偏向锁存在)
// markOopDesc::biased_lock_mask_in_place) 偏向锁的位
else if ((anticipated_bias_locking_value & markOopDesc::biased_lock_mask_in_place) != 0) { // 原始对象头部的偏向锁位发生改变,撤销偏向锁
// try revoke bias
// 尝试撤销偏向锁
markOop header = lockee->klass()->prototype_header();
if (hash != markOopDesc::no_hash) {
header = header->copy_set_hash(hash);
}
// CAS 将对象头从 mark 替换为 header 撤销偏向锁
if (lockee->cas_set_mark(header, mark) == mark) {
if (PrintBiasedLockingStatistics)
(*BiasedLocking::revoked_lock_entry_count_addr())++;
}
}
// 执行到此处说明 (anticipated_bias_locking_value & markOopDesc::biased_lock_mask_in_place) == 0
// 3. 在批量撤销偏向锁的时候需要更改 epoch 的值
// 此处 epoch 不为 0,说明偏向锁已被批量撤销,此处需要进行重偏向
else if ((anticipated_bias_locking_value & epoch_mask_in_place) !=0) {
// try rebias
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);
}
// CAS 重偏向
if (lockee->cas_set_mark(new_header, mark) == mark) {
if (PrintBiasedLockingStatistics)
(* BiasedLocking::rebiased_lock_entry_count_addr())++;
}
else {
// CAS 失败,发生了竞争,需要进入 monitorenter
CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
}
success = true;
}
// 4. 以上条件均不满足,说明开启了偏向锁,此时偏向锁的状态为匿名偏向锁,尝试 CAS 将其偏向为当前线程
// 匿名重偏向(偏向锁机制已经打开,但是还未被其他线程偏向)
else {
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);
}
markOop new_header = (markOop) ((uintptr_t) header | thread_ident);
// CAS 重偏向
if (lockee->cas_set_mark(new_header, header) == header) {
if (PrintBiasedLockingStatistics)
(* BiasedLocking::anonymously_biased_lock_entry_count_addr())++;
}
else {
// CAS 失败,发生了竞争,进入 monitorenter
CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
}
success = true;
}
}
// ====== if (mark->has_bias_pattern()) 结束 ===
// traditional lightweight locking
// 偏向锁获取失败,进入传统的轻量级锁
// ====== if (!success) 开始 ===
if (!success) {
// 构造一个无锁状态的对象头部(锁标志位:01)
markOop displaced = lockee->mark()->set_unlocked();
// 将上述构造的对象头部保存到当前线程的 BasicObjectLock 容器中
// BasicObjectLock* entry
entry->lock()->set_displaced_header(displaced);
// UseHeavyMonitors 若为 true,则直接使用重量级锁
// call_vm 默认为 false,忽略即可。
bool call_vm = UseHeavyMonitors;
// 尝试获取轻量级锁:
// 将对象头部原始信息保存在线程栈的 BasicObjectLock 容器中
// CAS 将对象头部替换为 BasicObjectLock 的地址,若替换成功表示上锁成功
// 替换值 entry,期望值 displaced,替换成功返回期望值 displaced(displaced 为无锁对象)
// cas_set_mark(markOop new_mark, markOop old_mark)
// Atomic::cmpxchg_ptr(new_mark, &_mark, old_mark)
if (lockee->cas_set_mark((markOop)entry, displaced) != displaced) {
// 如果失败,可能是当前线程轻量级锁重入
// 判断是否是锁重入,通过 is_lock_owned 方法查看对象头部的轻量级锁的 BasicObjectLock 是否属于当前线程,若是,则为轻量级锁重入。
if (THREAD->is_lock_owned((address) displaced->clear_lock_bits())) {
// 轻量级锁重入:由于 BasicObjectLock 是一个栈,所以只需要在栈底的 BasicObjectLock 保存替换头部即可,其他的 BasicObjectLock 不需要保存。因为只有最后一个重入锁释放才会替换掉头部。
entry->lock()->set_displaced_header(NULL);
} else { // 不是锁重入
// 偏向锁和轻量级锁获取失败,进入 monitorenter,进行锁升级
CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
}
}
}
// ====== if (!success) 结束 ===
UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);
} else {
// 如果没有找到空闲的 BasicObjectLock,则设置 more_monitors 标志位,由解释器分配新的 BasicObjectLock 并重试
istate->set_msg(more_monitors);
UPDATE_PC_AND_RETURN(0); // Re-execute
}
}
最后
以上就是深情电脑为你收集整理的【正本清源】Synchronized 源码全解之偏向锁的获取(基于 Openjdk12)的全部内容,希望文章能够帮你解决【正本清源】Synchronized 源码全解之偏向锁的获取(基于 Openjdk12)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复