性能问答>Synchronized锁性能是否会被新申请lockRecord时,拷贝原来的lockRecord列表而影响性能?>
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属性未被使用),来存放当前想要获取锁的对象。这里有三种情况:

  1. 有空的BasicObjectLock,继续往高地址位查找,最终把当前获取锁的对象,放到高地址位的BasicObjectLock中
  2. 发现一个空BasicObjectLock,继续往上,但又发现这是锁的重入,跳出循环,把当前获取锁的对象放入到前一个被找到的BasicObjectLock中
  3. 在没有找到空BasicObjectLock之前,发现是当前获取锁的对象是重入;或者遍历完也没有发现空BasicObjectLock,直接跳出循环新申请一个空BasicObjectLock,把未申请新slot之前的所有BasicObjectLock拷贝下移一个erntrysize,最后把当前获取锁的对象放到最高地址位的BasicObjectLock中

上面这个流程对吗?只有嵌套锁才会出现上面的情况对吗?我不知我对这个monitorenter理解是否正确。
为什么monitor block中新增一个BasicObjectLock之后,不直接把当前获取锁的对象放到这个新的BasicObjectLock中,而是整体拷贝移动原表达式栈的数据之后,再把新的锁记录插入到后面?移动拷贝如果是多余的,这样不就影响了锁的性能?

1019 阅读
请先登录,查看9条精彩评论吧
快去登录吧,你将获得
  • 浏览更多精彩评论
  • 和开发者讨论交流,共同进步