欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

從匯編代碼開(kāi)始全面解析synchronized還原最真實(shí)的偏向鎖

 更新時(shí)間:2022年02月11日 15:13:27   作者:自成溪  
這篇文章主要為大家介紹了從模板解釋器匯編源碼開(kāi)始分析還原最真實(shí)的偏向鎖實(shí)現(xiàn),解釋monitorenter字節(jié)碼命令的方法開(kāi)始,從匯編代碼開(kāi)始全面解析synchronized

前言

我們都知道java之所以跨平臺(tái)能力強(qiáng),是因?yàn)閖ava在編譯期沒(méi)有被編譯成機(jī)器碼,而是被編譯成字節(jié)碼。早期的jvm會(huì)將編譯好的字節(jié)碼翻譯成機(jī)器碼解釋執(zhí)行,我們?cè)趈vm的源碼中還可以看到早期的解釋器——bytecodeInterpreter.cpp(雖然已經(jīng)不再使用)。對(duì)于字節(jié)碼這種總數(shù)固定,解釋邏輯固定的命令,現(xiàn)代jvm將其執(zhí)行進(jìn)行了優(yōu)化,在jvm初始化的時(shí)候,直接將每個(gè)字節(jié)碼指令將要執(zhí)行的匯編代碼加載到內(nèi)存中,在運(yùn)行時(shí)執(zhí)行某段字節(jié)碼時(shí)直接調(diào)用內(nèi)存中對(duì)應(yīng)的匯編代碼即可,這樣的解釋器就時(shí)模板解釋器——templateTable.cpp。而synchronized修飾代碼塊時(shí),其編譯成字節(jié)碼后就是monitorenter和monitorexit(關(guān)于如何查看編譯后的字節(jié)碼可以查看筆者往期的博客)。

所以要想看現(xiàn)代jvm的synchronized實(shí)現(xiàn)還要從模板解釋器(templateTable)的monitorenter方法看起(網(wǎng)上許多文章都是從bytecodeInterpreter開(kāi)始分析,雖然大致邏輯一樣,更有甚者將偏向鎖撤銷邏輯硬是理解成偏向鎖加鎖邏輯,非常混亂),本文筆者就從模板解釋器匯編源碼開(kāi)始分析還原最真實(shí)的偏向鎖實(shí)現(xiàn),解釋monitorenter字節(jié)碼命令的方法開(kāi)始,從匯編代碼開(kāi)始全面解析synchronized。

一.TemplateTable::monitorenter()

關(guān)于這個(gè)monitorenter()方法,主要包括在方法棧幀中獲取lockRecord以及若lockRecord不夠則擴(kuò)容的邏輯,由于這部分代碼是將字節(jié)碼直接解釋成機(jī)器碼,所以以方法名的形式將機(jī)器碼封裝成了對(duì)應(yīng)的匯編命令,我們碰到的匯編方法將其當(dāng)成對(duì)應(yīng)的匯編命令即可(值得注意的是里面又很多jmp,jcc,jccb等跳轉(zhuǎn)指令,由于篇幅有限本文就不過(guò)多介紹,有興趣的讀者可以自行了解,本文就將其當(dāng)成跳轉(zhuǎn)指令),其他匯編命令也比較簡(jiǎn)單,這里就不過(guò)多介紹,讀者如果碰到相關(guān)不熟悉的命令可以自行搜索下相關(guān)概念,好了話不多說(shuō)我們直接看源碼:

void TemplateTable::monitorenter() {
  transition(atos, vtos);
  // 檢查對(duì)象是否為null,此時(shí)對(duì)象存在rax寄存器中
  __ null_check(rax);
  // rbp是堆棧寄存器,通常指向棧底
  // 棧幀中存在一個(gè)monitor數(shù)組用于保存鎖相關(guān)信息,又叫l(wèi)ockRecord(后面都統(tǒng)稱為lockRecord)
  // frame::interpreter_frame_monitor_block_top_offset和frame::interpreter_frame_initial_sp_offset 
  // 表示monitor top 和monitor bot偏移量
  // Address(x, j)表示距離x地址j偏移量的地址
  // 所以這里聲明的兩個(gè)變量我們可以簡(jiǎn)單理解為棧幀中的monitor top 和monitor bot地址
  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;
  // 初始化c_rarg1寄存器中的值(這里本質(zhì)是一個(gè)異或運(yùn)算)
  __ xorl(c_rarg1, c_rarg1); // points to free slot or NULL
  // 這部分代碼邏輯是循環(huán)從lockRecord數(shù)組中找到一個(gè)空的槽位,并將其放入c_rarg1寄存器中
  {
    Label entry, loop, exit;
    __ movptr(c_rarg3, monitor_block_top); 
    __ lea(c_rarg2, monitor_block_bot); 
    // 直接跳到entry標(biāo)簽位
    __ jmpb(entry);
    // 綁定loop標(biāo)簽開(kāi)始循環(huán)
    __ bind(loop);
    // 檢查當(dāng)前LockRecord是否被使用
    __ cmpptr(Address(c_rarg3, BasicObjectLock::obj_offset_in_bytes()), (int32_t) NULL_WORD);
    // 沒(méi)有被使用則將其放到c_rarg1
    __ cmov(Assembler::equal, c_rarg1, c_rarg3);
    // 檢查和當(dāng)前對(duì)象是否一樣
    __ cmpptr(rax, Address(c_rarg3, BasicObjectLock::obj_offset_in_bytes()));
    // 如果一樣則表示重入,跳出循環(huán)
    __ jccb(Assembler::equal, exit);
    // 否則則跳到下一個(gè)entry
    __ addptr(c_rarg3, entry_size);
    // 綁定entry標(biāo)簽
    __ bind(entry);
    // 比較c_rarg3與c_rarg2寄存器中的值,即是否相等
    __ cmpptr(c_rarg3, c_rarg2);
    // 若不等則跳到loop繼續(xù)循環(huán)
    __ jcc(Assembler::notEqual, loop);
    __ bind(exit);
  }
  //檢測(cè)一個(gè)空槽位是否被找到(如果是重入則不會(huì)跳轉(zhuǎn)會(huì)去新申請(qǐng))
  __ testptr(c_rarg1, c_rarg1);
  //找到則跳到 allocated標(biāo)簽
  __ jcc(Assembler::notZero, allocated); 
  // 如果沒(méi)有空的slot則申請(qǐng)一個(gè),這里還包括了申請(qǐng)后調(diào)整位置的邏輯
  {
    Label entry, loop;
    // 將lockrecord底部的指針?lè)诺絚_rarg1寄存器中
    __ movptr(c_rarg1, monitor_block_bot); 
    // 計(jì)算并移動(dòng)棧頂和棧底到新位置,均移動(dòng)entry_size(rsp寄存器指向棧頂)
    __ subptr(rsp, entry_size);            
    __ subptr(c_rarg1, entry_size);        
    // 設(shè)置新的棧頂位置和棧底位置分別到c_rarg3寄存器和monitor_block_bot地址上
    __ mov(c_rarg3, rsp);                  
    __ movptr(monitor_block_bot, c_rarg1); 
    // 跳到entry標(biāo)簽——為了先比較下然后開(kāi)始循環(huán)
    // c_rarg1則是新的空slot
    __ jmp(entry);
    __ bind(loop);
    // 這兩行是將老棧頂位置的值存到新棧頂位置
    __ movptr(c_rarg2, Address(c_rarg3, entry_size));                                                   
    __ movptr(Address(c_rarg3, 0), c_rarg2);          
    // 推進(jìn)到下一個(gè)位置
    __ addptr(c_rarg3, wordSize);                    
    __ bind(entry);
    __ cmpptr(c_rarg3, c_rarg1);            
    __ jcc(Assembler::notEqual, loop);      
                                            
  }
  // 綁定allocated標(biāo)簽
  __ bind(allocated);
  __ increment(r13);
  // 保存對(duì)象到lockRecord中,locrRecord對(duì)象有兩個(gè)屬性分別是對(duì)象指針和鎖
  // BasicObjectLock::obj_offset_in_bytes()也表示偏移量
  __ movptr(Address(c_rarg1, BasicObjectLock::obj_offset_in_bytes()), rax);
  // 加鎖方法
  __ lock_object(c_rarg1);
  // 檢查以確保該監(jiān)視器在鎖定后不會(huì)導(dǎo)致堆棧溢出
  __ save_bcp();  
  __ generate_stack_overflow_check(0);
  // 調(diào)用下一個(gè)指令
  __ dispatch_next(vtos);
}

 我們看到下一個(gè)方法是_lock_object()方法,這個(gè)方法我們等下在分析,在這之前筆者先介紹下我們?cè)创a中看到的lockRecord,其實(shí)時(shí)basicLock.cpp中的BasicObjectLock類:

//只有兩個(gè)屬性
class BasicObjectLock VALUE_OBJ_CLASS_SPEC {
 private:
  //鎖對(duì)象
  BasicLock _lock;                                   
  //表示持有鎖的對(duì)象
  oop       _obj;             
}
//再來(lái)看看鎖對(duì)象——只有一個(gè)屬性
class BasicLock VALUE_OBJ_CLASS_SPEC {
 private:
  //markword一般保存的是持有鎖對(duì)象的markword
  volatile markOop _displaced_header;
}

可以看到lockRecord是用于關(guān)聯(lián)對(duì)象和鎖的關(guān)系的,如果在當(dāng)前方法中有加鎖的對(duì)象,就會(huì)在解釋棧幀中添加一個(gè)lockRecord用于記錄相應(yīng)的對(duì)象和鎖的關(guān)系,不僅如此lockRecord還會(huì)隱式的鎖重入的計(jì)數(shù)器,當(dāng)發(fā)生重入時(shí),就會(huì)為同一個(gè)對(duì)象創(chuàng)建多個(gè)lockRecord。從源碼中我們也可以看到在解釋的方法執(zhí)行期間,lockRecord的數(shù)組會(huì)根據(jù)持有的鎖數(shù)量增長(zhǎng)或縮小。

二.lock_object():

接下來(lái)我們來(lái)一起看看lock_object()方法:

//在interp_masm_x86_64.cpp文件中
void InterpreterMacroAssembler::lock_object(Register lock_reg) {
  assert(lock_reg == c_rarg1, "The argument is only for looks. It must be c_rarg1");
  //判斷是否強(qiáng)制使用重鎖,默認(rèn)是false
  if (UseHeavyMonitors) {
    call_VM(noreg,
            CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter),
            lock_reg);
  } else {
    //定義完成標(biāo)簽
    Label done;
    const Register swap_reg = rax;
    const Register obj_reg = c_rarg3;
    //聲明一些偏移量
    const int obj_offset = BasicObjectLock::obj_offset_in_bytes();
    const int lock_offset = BasicObjectLock::lock_offset_in_bytes ();
    const int mark_offset = lock_offset + BasicLock::displaced_header_offset_in_bytes();
    Label slow_case;
    // 傳入的basicObjectLock中的對(duì)象地址存到obj_reg中,即c_rarg3寄存器中
    movptr(obj_reg, Address(lock_reg, obj_offset));
    //使用偏向鎖
    if (UseBiasedLocking) {
      //偏向鎖加鎖方法
      biased_locking_enter(lock_reg, obj_reg, swap_reg, rscratch1, false, done, &slow_case);
    }
    //后面的方法是關(guān)于偏向鎖撤銷和升級(jí)的,不是本文重點(diǎn),本文先略過(guò)
    movl(swap_reg, 1);
    orptr(swap_reg, Address(obj_reg, 0));
    movptr(Address(lock_reg, mark_offset), swap_reg);
    assert(lock_offset == 0,
           "displached header must be first word in BasicObjectLock");
    if (os::is_MP()) lock();
    cmpxchgptr(lock_reg, Address(obj_reg, 0));
    if (PrintBiasedLockingStatistics) {
      cond_inc32(Assembler::zero,
                 ExternalAddress((address) BiasedLocking::fast_path_entry_count_addr()));
    }
    jcc(Assembler::zero, done);
    subptr(swap_reg, rsp);
    andptr(swap_reg, 7 - os::vm_page_size());
    movptr(Address(lock_reg, mark_offset), swap_reg);
    if (PrintBiasedLockingStatistics) {
      cond_inc32(Assembler::zero,
                 ExternalAddress((address) BiasedLocking::fast_path_entry_count_addr()));
    }
    jcc(Assembler::zero, done);
    bind(slow_case);
    call_VM(noreg,
            CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter),
            lock_reg);
    bind(done);
  }
}

三.biased_locking_enter()

1).參數(shù)

lock_object()調(diào)用的biased_locking_enter()方法中才是真正偏向鎖邏輯,我們這里介紹下傳入的幾個(gè)參數(shù)(便于我們后續(xù)分析):

lock_reg:lock_object方法傳入的空的lockRecord,其內(nèi)部已經(jīng)保存了當(dāng)前要加鎖的對(duì)象

obj_reg:持有對(duì)象的寄存器,其內(nèi)部保存了將要加鎖的對(duì)象

swap_reg:目前是一個(gè)空的寄存器,會(huì)用于保存中間值

rscratch1: 臨時(shí)寄存器,用于保存中間值

done:done標(biāo)簽方便直接跳到方法結(jié)束

&slow_case:slow_case標(biāo)簽方便直接跳到鎖升級(jí)邏輯

比較重要的是前兩個(gè)參數(shù),分別保存我們要判斷的lockRecord和對(duì)象,后面兩個(gè)參數(shù)其實(shí)是方便我們直接跳轉(zhuǎn)到對(duì)應(yīng)邏輯的標(biāo)簽。

2).概念

這個(gè)方法中還會(huì)涉及到一些概念,網(wǎng)上也有一些介紹,筆者先簡(jiǎn)單介紹下,方便大家閱讀:

markword:一般用二進(jìn)制表示,對(duì)象頭中的markword,主要用來(lái)表示對(duì)象的線程鎖狀態(tài),另外還可以用來(lái)配合GC、存放該對(duì)象的hashCode

這張圖就表示markword的幾個(gè)狀態(tài)。

klassword:一個(gè)指向方法區(qū)中Class信息的指針一般用二進(jìn)制表示,通過(guò)指針可以獲取其相應(yīng)的klass對(duì)象(即方法區(qū)中表示class信息的對(duì)象)

偏向模式:表示對(duì)象是否當(dāng)前是偏向狀態(tài),即markword最后三位是否是101,這里需要注意的是不光普通對(duì)象具有偏向模式,klass對(duì)象也有偏向模式,具體可以在systemDictionary.cpp 的update_dictionary方法中可以看到,所有創(chuàng)建的klass鎖狀態(tài)起始是001,然后會(huì)被更新為101。創(chuàng)建普通對(duì)象時(shí)會(huì)將klass中的markword填充到oop對(duì)象中。Klass對(duì)象除了再剛開(kāi)始創(chuàng)建時(shí)鎖狀態(tài)時(shí)001,再進(jìn)行批量偏向鎖撤銷的時(shí)候也會(huì)恢復(fù)成001(這部分不是本文重點(diǎn),具體細(xì)節(jié)就先不分析)。所以當(dāng)一個(gè)對(duì)象是偏向模式時(shí),其不一定是持有偏向鎖的,因?yàn)閷?duì)象剛創(chuàng)建出來(lái)其markword后三位即101,需要通過(guò)線程ID,epoch來(lái)判斷其是否持有偏向鎖。

3).源碼

讓我們繼續(xù)看biased_locking_enter()方法:

//調(diào)用的是macroAssembler_x86.cpp中的方法
int MacroAssembler::biased_locking_enter(Register lock_reg,
                                         Register obj_reg,
                                         Register swap_reg,
                                         Register tmp_reg,
                                         bool swap_reg_contains_mark,
                                         Label& done,
                                         Label* slow_case,
                                         BiasedLockingCounters* counters) {
  ......
  bool need_tmp_reg = false;
  //noreg是一個(gè)宏,表示空的寄存器
  if (tmp_reg == noreg) {
    need_tmp_reg = true;
    tmp_reg = lock_reg;
  } else {
    assert_different_registers(lock_reg, obj_reg, swap_reg, tmp_reg);
  }
  //定義一些地址,分別是markword,klass和lockRecord中的鎖對(duì)象地址
  Address mark_addr      (obj_reg, oopDesc::mark_offset_in_bytes());
  Address klass_addr     (obj_reg, oopDesc::klass_offset_in_bytes());
  Address saved_mark_addr(lock_reg, 0);
  // 偏向鎖邏輯開(kāi)始
  // 分支1:查看當(dāng)前對(duì)象是否開(kāi)啟偏向模式
  Label cas_label;
  int null_check_offset = -1;
  //swap_reg_contains_mark傳入的是false,表示swap_reg不包括markword地址
  if (!swap_reg_contains_mark) {
    null_check_offset = offset();
    //將對(duì)象的markword放入swap_reg
    movl(swap_reg, mark_addr);
  }
  if (need_tmp_reg) {
    push(tmp_reg);
  }
  //將對(duì)象的markword放入tmp_reg
  movl(tmp_reg, swap_reg);
  //取其鎖標(biāo)記位(與指令)
  //markOopDesc::biased_lock_mask_in_place=111 這里是取markword的后三位到tmp_reg寄存器中
  andl(tmp_reg, markOopDesc::biased_lock_mask_in_place);
  //判斷是否有鎖(比較指令)
  //markOopDesc::biased_lock_pattern=101
  cmpl(tmp_reg, markOopDesc::biased_lock_pattern);
  if (need_tmp_reg) {
    pop(tmp_reg);
  }
  //如果不相等則表示沒(méi)有開(kāi)啟對(duì)象偏向模式(即已經(jīng)是輕量級(jí)鎖)則跳到cas_label標(biāo)簽到方法末尾
  jcc(Assembler::notEqual, cas_label);
  // 分支2:相等則表示對(duì)象markword后三位是101即現(xiàn)在對(duì)象是偏向鎖模式(但不一定持有偏向鎖)
  // 這部分的邏輯是將線程id和epoch信息做比對(duì),判斷是否已經(jīng)持有偏向鎖
  movl(saved_mark_addr, swap_reg);
  if (need_tmp_reg) {
    push(tmp_reg);
  }
  //獲取線程id
  get_thread(tmp_reg);
  //對(duì)象的markword與線程id異或,若線程id部分一樣則線程id部分會(huì)變成0
  xorl(swap_reg, tmp_reg);
  if (swap_reg_contains_mark) {
    null_check_offset = offset();
  }
  //將klass放入tmp_reg寄存器
  movl(tmp_reg, klass_addr);
  //與klass的markword異或,若兩者同位部分一樣則同位會(huì)變成0,這里是為了判斷epoch和鎖標(biāo)志位是否與klass一樣
  xorl(swap_reg, Address(tmp_reg, Klass::prototype_header_offset()));
  //設(shè)置分代年齡掩碼即年齡為0
  andl(swap_reg, ~((int) markOopDesc::age_mask_in_place));
  if (need_tmp_reg) {
    pop(tmp_reg);
  }
  if (counters != NULL) {
    cond_inc32(Assembler::zero,
               ExternalAddress((address)counters->biased_lock_entry_count_addr()));
  }
  //前面已經(jīng)處理過(guò)markword,將其關(guān)鍵信息已經(jīng)存入swap_reg中,后面只使用swap_reg進(jìn)行判斷
  //如果swap等于0,則表明線程id是本線程id,且epoch和鎖標(biāo)志位都與klass中的一樣,即已經(jīng)偏向本線程,跳到加鎖結(jié)束
  jcc(Assembler::equal, done);
  //定義撤銷偏向鎖標(biāo)簽
  Label try_revoke_bias;
  //定義重偏向鎖標(biāo)簽
  Label try_rebias;
  //若不等則證明線程id,epoch和鎖標(biāo)志位有不一樣的
  //分支3:先判斷鎖標(biāo)志位,即判斷類的偏向模式是否是關(guān)閉
  //test可以理解為與運(yùn)算
  //因?yàn)橹耙呀?jīng)判斷過(guò)對(duì)象的是偏向模式,而klass與對(duì)象的鎖標(biāo)記位不等,則證明klass對(duì)象不是偏向模式
  //如果類偏向模式是關(guān)閉,表明正在進(jìn)行批量撤銷偏向鎖的行為,即正在進(jìn)行鎖升級(jí)
  //所以需要cas替換修復(fù)對(duì)象的markword,修復(fù)成類的markword,跳到撤銷標(biāo)簽
  testl(swap_reg, markOopDesc::biased_lock_mask_in_place);
  jcc(Assembler::notZero, try_revoke_bias);
  //分支4:再判斷是否是epoch過(guò)期,過(guò)期則跳到重偏向標(biāo)簽
  testl(swap_reg, markOopDesc::epoch_mask_in_place);
  jcc(Assembler::notZero, try_rebias);
  //分支5:到這里只剩線程id并不是本線程,進(jìn)行一次cas替換嘗試加偏向鎖
  //將對(duì)象的markword讀到swap_reg中
  movl(swap_reg, saved_mark_addr);
  //進(jìn)行與運(yùn)算,獲取對(duì)象markword的鎖標(biāo)志位和age,epoch用來(lái)構(gòu)造一個(gè)新的帶鎖的markword
  andl(swap_reg,
       markOopDesc::biased_lock_mask_in_place | markOopDesc::age_mask_in_place | markOopDesc::epoch_mask_in_place);
  if (need_tmp_reg) {
    push(tmp_reg);
  }
  get_thread(tmp_reg);
  //將線程id也加入到構(gòu)造的markword中
  orl(tmp_reg, swap_reg);
  //判斷是否是多核cpu如果是則加鎖——執(zhí)行Lock命令
  if (os::is_MP()) {
    lock();
  }
  //cas替換對(duì)象的對(duì)象的markword為剛剛構(gòu)造的持有鎖信息的markword
  //Address(obj_reg, 0)表示對(duì)象的markword位置
  cmpxchgptr(tmp_reg, Address(obj_reg, 0));
  if (need_tmp_reg) {
    pop(tmp_reg);
  }
  if (counters != NULL) {
    cond_inc32(Assembler::zero,
               ExternalAddress((address)counters->anonymously_biased_lock_entry_count_addr()));
  }
  //cas不為0則證明偏向我們失敗,意味著有另一個(gè)線程成功偏向,有競(jìng)爭(zhēng)
  //則進(jìn)入slow邏輯,跳轉(zhuǎn)到slow_case標(biāo)簽,執(zhí)行撤銷升級(jí)邏輯
  if (slow_case != NULL) {
    jcc(Assembler::notZero, *slow_case);
  }
  //成功證明已經(jīng)偏向成功,跳轉(zhuǎn)到done標(biāo)簽
  jmp(done);
  //epoch過(guò)期,重新偏向標(biāo)簽
  bind(try_rebias);
  if (need_tmp_reg) {
    push(tmp_reg);
  }
  //獲取當(dāng)前線程ID
  get_thread(tmp_reg);
  movl(swap_reg, klass_addr);
  //或運(yùn)算,以klass的markword為基礎(chǔ)和線程id組合構(gòu)成新的markword
  orl(tmp_reg, Address(swap_reg, Klass::prototype_header_offset()));
  movl(swap_reg, saved_mark_addr);
  if (os::is_MP()) {
    lock();
  }
  //將新構(gòu)造的markword cas替換 對(duì)象的markword
  cmpxchgptr(tmp_reg, Address(obj_reg, 0));
  if (need_tmp_reg) {
    pop(tmp_reg);
  }
  if (counters != NULL) {
    cond_inc32(Assembler::zero,
               ExternalAddress((address)counters->rebiased_lock_entry_count_addr()));
  }
  //偏向失敗則證明有另外的線程偏向成功,需要撤銷偏向
  if (slow_case != NULL) {
    jcc(Assembler::notZero, *slow_case);
  }
  //跳到結(jié)束
  jmp(done);
  //撤銷偏向,將對(duì)象markword重置為klass(類)的markword
  //這里只有判斷類的markword不是偏向標(biāo)記才會(huì)進(jìn)入,所以會(huì)將對(duì)象的markword重置為非偏向標(biāo)記
  bind(try_revoke_bias);
  movl(swap_reg, saved_mark_addr);
  if (need_tmp_reg) {
    push(tmp_reg);
  }
  //獲取對(duì)象klass的markword
  movl(tmp_reg, klass_addr);
  movl(tmp_reg, Address(tmp_reg, Klass::prototype_header_offset()));
  if (os::is_MP()) {
    lock();
  }
  //用對(duì)象klass的markword cas替換對(duì)象的markword
  cmpxchgptr(tmp_reg, Address(obj_reg, 0));
  if (need_tmp_reg) {
    pop(tmp_reg);
  }
  //無(wú)論cas的結(jié)果成功與否,都證明有線程撤銷成功,所以繼續(xù)執(zhí)行
  if (counters != NULL) {
    cond_inc32(Assembler::zero,
               ExternalAddress((address)counters->revoked_lock_entry_count_addr()));
  }
  bind(cas_label);
  return null_check_offset;
}

看完了源碼我們可以這樣理解偏向鎖,添加偏向鎖的過(guò)程即是在對(duì)象處于可偏向模式時(shí),在對(duì)象的markword中cas替換對(duì)應(yīng)的線程id標(biāo)記位,即表示當(dāng)前線程持有了對(duì)象的偏向鎖。完整的偏向鎖處理邏輯已經(jīng)分析完了,這里面分支比較多,我們來(lái)畫(huà)圖幫助理解下:

從圖中我們可以看到若對(duì)象持有偏向鎖且鎖不是偏向本線程,則會(huì)最少會(huì)進(jìn)行一次cas替換,若cas替換失敗則會(huì)進(jìn)入偏向鎖的撤銷升級(jí)邏輯。因?yàn)槠蜴icas替換后會(huì)進(jìn)入撤銷升級(jí)的邏輯,所以從效率上看偏向鎖更適合一個(gè)線程不斷的獲取鎖的場(chǎng)景,而事實(shí)上偏向鎖正是設(shè)計(jì)用于應(yīng)對(duì)一個(gè)線程獲取鎖的場(chǎng)景。

當(dāng)然synchronized的執(zhí)行邏輯還沒(méi)有結(jié)束,本篇博客我們只著重分析偏向鎖相關(guān)邏輯。筆者后續(xù)還會(huì)繼續(xù)分析synchronized的輕量級(jí)鎖和重量級(jí)鎖的邏輯,盡量還原最原汁原味的synchronized。

以上就是從匯編代碼開(kāi)始全面解析synchronized還原最真實(shí)的偏向鎖的詳細(xì)內(nèi)容,更多關(guān)于匯編代碼解析synchronized還原偏向鎖的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • UEFI開(kāi)發(fā)基礎(chǔ)匯編代碼的使用

    UEFI開(kāi)發(fā)基礎(chǔ)匯編代碼的使用

    這篇文章主要為大家介紹了UEFI開(kāi)發(fā)基礎(chǔ)匯編代碼使用,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • 匯編語(yǔ)言中test和cmp有什么區(qū)別

    匯編語(yǔ)言中test和cmp有什么區(qū)別

    匯編語(yǔ)言(assembly language)是一種用于電子計(jì)算機(jī)、微處理器、微控制器或其他可編程器件的低級(jí)語(yǔ)言,亦稱為符號(hào)語(yǔ)言。這篇文章主要介紹了匯編語(yǔ)言中test和cmp有什么區(qū)別,需要的朋友可以參考下
    2020-01-01
  • 淺析shellcode 反匯編模擬運(yùn)行及調(diào)試方法

    淺析shellcode 反匯編模擬運(yùn)行及調(diào)試方法

    這篇文章主要介紹了shellcode 反匯編,模擬運(yùn)行以及調(diào)試方法,本文給大介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-02-02
  • 匯編語(yǔ)言之寄存器(詳細(xì)介紹)

    匯編語(yǔ)言之寄存器(詳細(xì)介紹)

    這篇文章主要介紹了匯編語(yǔ)言之寄存器(詳細(xì)介紹),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2020-01-01
  • asm基礎(chǔ)——匯編指令之in/out指令

    asm基礎(chǔ)——匯編指令之in/out指令

    這篇文章主要介紹了asm基礎(chǔ)——匯編指令之in/out指令,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-01-01
  • 匯編語(yǔ)言中的segment

    匯編語(yǔ)言中的segment

    segment是段的意思,是段定義偽指令,一個(gè)正常的應(yīng)用程序被由若干個(gè) segment組成,接下來(lái)通過(guò)本文給大家介紹匯編語(yǔ)言中的segment,需要的朋友可以參考下
    2020-01-01
  • 匯編語(yǔ)言偽指令和匯編指令的區(qū)別

    匯編語(yǔ)言偽指令和匯編指令的區(qū)別

    指令是控制程序運(yùn)行時(shí)的機(jī)器代碼運(yùn)作的,是CPU執(zhí)行的依據(jù),編程、編譯、執(zhí)行都是有效的。偽指令不直接控制運(yùn)行時(shí)刻的機(jī)器,但是控制翻譯程序如何生成機(jī)器指令代碼,感興趣的朋友跟隨小編一起看看吧
    2020-01-01
  • 匯編語(yǔ)言:x86匯編指令大全及其注意事項(xiàng)

    匯編語(yǔ)言:x86匯編指令大全及其注意事項(xiàng)

    用最精煉的語(yǔ)言,記錄匯編語(yǔ)言中所有常用或不常用或極其重要的匯編指令及其注意事項(xiàng),以方便自己和讀者進(jìn)行查閱,如有錯(cuò)誤和不足請(qǐng)?jiān)谠u(píng)論區(qū)指出
    2021-10-10
  • 匯編 函數(shù)調(diào)用的實(shí)現(xiàn)

    匯編 函數(shù)調(diào)用的實(shí)現(xiàn)

    這篇文章主要介紹了匯編 函數(shù)調(diào)用的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-02-02
  • 匯編語(yǔ)言之實(shí)現(xiàn)發(fā)出各種聲音

    匯編語(yǔ)言之實(shí)現(xiàn)發(fā)出各種聲音

    本文給大家分享一個(gè)練手小項(xiàng)目,響鈴設(shè)計(jì)項(xiàng)目,列出了響鈴,機(jī)槍聲音,音樂(lè)播放器的代碼,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧
    2021-11-11

最新評(píng)論