9回复
1年前
Synchronized锁性能是否会被新申请lockRecord时,拷贝原来的lockRecord列表而影响性能?
各位大佬好,读马智大佬的书,他推荐过HeapDump,我就针对 HotSpot的模板解释器,深入之后带来的问题,主要是synchronized
关键字的字节码monitorenter
的逻辑:
void TemplateTable::monitorenter() {
transition(atos, vtos);
// check for NULL object
__ null_check(rax);
const Address monitor_block_top(
rbp, frame::interpreter_frame_monitor_block_top_offset * wordSize);
const Address monitor_block_bot(
rbp, frame::interpreter_frame_initial_sp_offset * wordSize);
const int entry_size = frame::interpreter_frame_monitor_size() * wordSize;
Label allocated;
// initialize entry pointer
__ xorl(c_rarg1, c_rarg1); // points to free slot or NULL
// find a free slot in the monitor block (result in c_rarg1)
{
Label entry, loop, exit;
__ movptr(c_rarg3, monitor_block_top); // points to current entry,
// starting with top-most entry
__ lea(c_rarg2, monitor_block_bot); // points to word before bottom
// of monitor block
__ jmpb(entry);
__ bind(loop);
// check if current entry is used
__ cmpptr(Address(c_rarg3, BasicObjectLock::obj_offset_in_bytes()), (int32_t) NULL_WORD);
// if not used then remember entry in c_rarg1
__ cmov(Assembler::equal, c_rarg1, c_rarg3);
// check if current entry is for same object
__ cmpptr(rax, Address(c_rarg3, BasicObjectLock::obj_offset_in_bytes()));
// if same object then stop searching
__ jccb(Assembler::equal, exit);
// otherwise advance to next entry
__ addptr(c_rarg3, entry_size);
__ bind(entry);
// check if bottom reached
__ cmpptr(c_rarg3, c_rarg2);
// if not at bottom then check this entry
__ jcc(Assembler::notEqual, loop);
__ bind(exit);
}
__ testptr(c_rarg1, c_rarg1); // check if a slot has been found
__ jcc(Assembler::notZero, allocated); // if found, continue with that one
// allocate one if there's no free slot
{
Label entry, loop;
// 1. compute new pointers // rsp: old expression stack top
__ movptr(c_rarg1, monitor_block_bot); // c_rarg1: old expression stack bottom
__ subptr(rsp, entry_size); // move expression stack top
__ subptr(c_rarg1, entry_size); // move expression stack bottom
__ mov(c_rarg3, rsp); // set start value for copy loop
__ movptr(monitor_block_bot, c_rarg1); // set new monitor block bottom
__ jmp(entry);
// 2. move expression stack contents
__ bind(loop);
__ movptr(c_rarg2, Address(c_rarg3, entry_size)); // load expression stack
// word from old location
__ movptr(Address(c_rarg3, 0), c_rarg2); // and store it at new location
__ addptr(c_rarg3, wordSize); // advance to next word
__ bind(entry);
__ cmpptr(c_rarg3, c_rarg1); // check if bottom reached
__ jcc(Assembler::notEqual, loop); // if not at bottom then
// copy next word
}
// call run-time routine
// c_rarg1: points to monitor entry
__ bind(allocated);
// Increment bcp to point to the next bytecode, so exception
// handling for async. exceptions work correctly.
// The object has already been poped from the stack, so the
// expression stack looks correct.
__ increment(r13);
// store object
__ movptr(Address(c_rarg1, BasicObjectLock::obj_offset_in_bytes()), rax);
__ lock_object(c_rarg1);
// check to make sure this monitor doesn't cause stack overflow after locking
__ save_bcp(); // in case of exception
__ generate_stack_overflow_check(0);
// The bcp has already been incremented. Just need to dispatch to
// next instruction.
__ dispatch_next(vtos);
}
下面是monitorenter的模板解释器,它的作用是:
在当前线程栈的monitor block区域遍历查找(低地址—>高地址)一个空的BasicObjectLock(obj属性未被使用),来存放当前想要获取锁的对象。这里有三种情况:
- 有空的BasicObjectLock,继续往高地址位查找,最终把当前获取锁的对象,放到高地址位的BasicObjectLock中
- 发现一个空BasicObjectLock,继续往上,但又发现这是锁的重入,跳出循环,把当前获取锁的对象放入到前一个被找到的BasicObjectLock中
- 在没有找到空BasicObjectLock之前,发现是当前获取锁的对象是重入;或者遍历完也没有发现空BasicObjectLock,直接跳出循环新申请一个空BasicObjectLock,把未申请新slot之前的所有BasicObjectLock拷贝下移一个erntrysize,最后把当前获取锁的对象放到最高地址位的BasicObjectLock中
上面这个流程对吗?只有嵌套锁才会出现上面的情况对吗?我不知我对这个monitorenter理解是否正确。
为什么monitor block中新增一个BasicObjectLock之后,不直接把当前获取锁的对象放到这个新的BasicObjectLock中,而是整体拷贝移动原表达式栈的数据之后,再把新的锁记录插入到后面?移动拷贝如果是多余的,这样不就影响了锁的性能?
1019 阅读