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

Java 鎖粗化與循環(huán)問題

 更新時(shí)間:2019年04月23日 09:28:30   作者:唐尤華  
這篇文章主要介紹了Java 鎖粗化與循環(huán)的相關(guān)知識(shí),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

1. 寫在前面

“JVM 解剖公園”是一個(gè)持續(xù)更新的系列迷你博客,閱讀每篇文章一般需要5到10分鐘。限于篇幅,僅對(duì)某個(gè)主題按照問題、測(cè)試、基準(zhǔn)程序、觀察結(jié)果深入講解。因此,這里的數(shù)據(jù)和討論可以當(dāng)軼事看,并沒有做一致性、寫作風(fēng)格、句法和語義錯(cuò)誤、重復(fù)或一致性檢查。如果選擇采信文中內(nèi)容,風(fēng)險(xiǎn)自負(fù)。

Aleksey Shipilёv,JVM 性能極客

推特 @shipilev

問題、評(píng)論、建議發(fā)送到 aleksey@shipilev.net

譯注:鎖粗化(Lock Coarsening)。鎖粗化是合并使用相同鎖對(duì)象的相鄰?fù)綁K的過程。如果編譯器不能使用鎖省略(Lock Elision)消除鎖,那么可以使用鎖粗化來減少開銷。

2. 問題

眾所周知,Hotspot 確實(shí)進(jìn)行了鎖粗化優(yōu)化,可以有效合并幾個(gè)相鄰?fù)綁K,從而降低鎖開銷。能夠把下面的代碼

synchronized (obj) {
 // 語句 1
}
synchronized (obj) {
 // 語句 2
}

轉(zhuǎn)化為

synchronized (obj) {
 // 語句 1
 // 語句 2
}

問題來了,Hotspot 能否對(duì)循環(huán)進(jìn)行這種優(yōu)化?例如,把

for (...) {
 synchronized (obj) {
  // 一些操作
 }
}

優(yōu)化成下面這樣?

synchronized (this) {
 for (...) {
   // 一些操作
 }
}

理論上,沒有什么能阻止我們這樣做,甚至可以把這種優(yōu)化看作只針對(duì)鎖的優(yōu)化,像 loop unswitching 一樣。然而,缺點(diǎn)是可能把鎖優(yōu)化后變得過粗,線程在執(zhí)行循環(huán)時(shí)會(huì)占據(jù)所有的鎖。

譯注:Loop unswitching 是一種編譯器優(yōu)化技術(shù)。通過復(fù)制循環(huán)主體,在 if 和 else 語句中放一份循環(huán)體代碼,實(shí)現(xiàn)將條件句的內(nèi)部循環(huán)移到循環(huán)外部,進(jìn)而提高循環(huán)的并行性。由于處理器可以快速運(yùn)算矢量,因此執(zhí)行速度得到提升。

3. 實(shí)驗(yàn)

要回答這個(gè)問題,最簡單的辦法就是找到 Hotspot 優(yōu)化的證據(jù)。幸運(yùn)的是,有了 JMH 幫助這項(xiàng)工作變得非常簡單。JMH 不僅在構(gòu)建基準(zhǔn)測(cè)試時(shí)有用,并且在分析基準(zhǔn)測(cè)試方面同樣好用。讓我們從一個(gè)簡單的基準(zhǔn)測(cè)試開始:

@Fork(..., jvmArgsPrepend = {"-XX:-UseBiasedLocking"})
@State(Scope.Benchmark)
public class LockRoach {
  int x;
  @Benchmark
  @CompilerControl(CompilerControl.Mode.DONT_INLINE)
  public void test() {
    for (int c = 0; c < 1000; c++) {
      synchronized (this) {
        x += 0x42;
      }
    }
  }
}

(完整的源代碼參見這里 ,請(qǐng)查看原文鏈接)

這里有一些重要的技巧:

使用 -XX:-UseBiasedLocking 禁用偏向鎖(Biased Lock)可以避免啟動(dòng)時(shí)間過長。由于偏向鎖不會(huì)立即啟動(dòng),在初始化階段要等待5秒鐘(參見 BiasedLockingStartupDelay 選項(xiàng))
禁用 @Benchmark 方法內(nèi)聯(lián)操作可以幫助我們從反匯編中分離相關(guān)內(nèi)容
加上“魔數(shù)” 0x42 有助于快速從反匯編中定位加法操作

譯注:偏向鎖(Biased Locking)。盡管 CAS 原子指令相對(duì)于重量級(jí)鎖來說開銷比較小,但還是存在非常可觀的本地延遲,為了在無鎖競爭的情況下避免取鎖獲過程中執(zhí)行不必要的 CAS 原子指令提出了偏向鎖技術(shù)。
論文 Quickly Reacquirable Locks ,作者 Dave Dice、Mark Moir、William Scherer III。

運(yùn)行環(huán)境 i7 4790K、Linux x86_64、JDK EA 9b156:

Benchmark            Mode  Cnt      Score    Error  Units
LockRoach.test       avgt    5   5331.617 ± 19.051  ns/op

從上面運(yùn)行數(shù)據(jù)能分析出什么結(jié)果?什么都看不出來,對(duì)吧?我們需要調(diào)查背后到底發(fā)生了什么。這時(shí) -prof perfasm 配置可以派上用場,它能顯示生成代碼中的熱點(diǎn)區(qū)域。用默認(rèn)設(shè)置運(yùn)行,能夠發(fā)現(xiàn)最熱的指令是加鎖 lock cmpxchg(CAS),而且只打印指令附近的代碼。-prof perfasm:mergeMargin=1000 配置可以將這些熱點(diǎn)區(qū)域合并保存為輸出片段,乍看之下可能覺得有點(diǎn)恐怖。

進(jìn)一步分析得出連續(xù)的跳轉(zhuǎn)指令是鎖定或解鎖,注意循環(huán)次數(shù)最多的代碼(第一列),可以看到最熱的循環(huán)像下面這樣:

↗ 0x00007f455cc708c1: lea  0x20(%rsp),%rbx
 │     < 省略若干代碼,進(jìn)入 monitor >   ; <--- coarsened(粗化)!
 │ 0x00007f455cc70918: mov  (%rsp),%r10    ; 加載 $this
 │ 0x00007f455cc7091c: mov  0xc(%r10),%r11d  ; 加載 $this.x
 │ 0x00007f455cc70920: mov  %r11d,%r10d    ; ...hm...
 │ 0x00007f455cc70923: add  $0x42,%r10d    ; ...hmmm...
 │ 0x00007f455cc70927: mov  (%rsp),%r8     ; ...hmmmmm!...
 │ 0x00007f455cc7092b: mov  %r10d,0xc(%r8)   ; LOL Hotspot,冗余存儲(chǔ),下面省略兩行
 │ 0x00007f455cc7092f: add  $0x108,%r11d    ; 加 0x108 = 0x42 * 4 <-- 展開4次
 │ 0x00007f455cc70936: mov  %r11d,0xc(%r8)   ; 把 $this.x 回省略若干代碼,退出 monitor >   ; <--- coarsened(粗化)!
 │ 0x00007f455cc709c6: add  $0x4,%ebp     ; c += 4  <--- 展開4次
 │ 0x00007f455cc709c9: cmp  $0x3e5,%ebp    ; c < 1000?
 ╰ 0x00007f455cc709cf: jl   0x00007f455cc708c1

哈哈。循環(huán)似乎被展開了4次,然后這4個(gè)迭代中實(shí)現(xiàn)鎖粗化!為了排除循環(huán)展開對(duì)鎖粗化的影響,我們可以通過-XX:LoopUnrollLimit=1 配置裁剪循環(huán)展開,再次量化受限后的粗化性能。

譯注:Loop unrolling(循環(huán)展開),也稱 Loop unwinding,是一種循環(huán)轉(zhuǎn)換技術(shù)。它試圖以犧牲二進(jìn)制大小為代價(jià)優(yōu)化程序的執(zhí)行速度,這種方法被稱為時(shí)空折衷。轉(zhuǎn)換可以由程序員手動(dòng)執(zhí)行,也可以由編譯器優(yōu)化。

Benchmark      Mode Cnt   Score  Error Units
# Default
LockRoach.test    avgt  5  5331.617 ± 19.051 ns/op
# -XX:LoopUnrollLimit=1
LockRoach.test    avgt  5 20679.043 ± 3.133 ns/op

哇,性能提升了4倍!顯而易見的,因?yàn)槲覀円呀?jīng)觀察到最熱的指令是加鎖 lock cmpxchg。當(dāng)然,4倍后的粗化鎖意味著4倍吞吐量。非??幔覀兪遣皇强梢孕汲晒?,然后繼續(xù)前進(jìn)?還沒有。我們必須驗(yàn)證禁用循環(huán)展開真正提供了我們想要進(jìn)行比較的內(nèi)容。perfasm 的結(jié)果似乎表明它含有類似的熱點(diǎn)循環(huán),只是跨了一大步。

↗ 0x00007f964d0893d2: lea  0x20(%rsp),%rbx
 │     < 省略若干代碼,進(jìn)入 monitor >
 │ 0x00007f964d089429: mov  (%rsp),%r10    ; 加載 $this
 │ 0x00007f964d08942d: addl  $0x42,0xc(%r10)  ; $this.x += 0x42
 │     < 省略若干代碼,退出 monitor >
 │ 0x00007f964d0894be: inc  %ebp        ; c++
 │ 0x00007f964d0894c0: cmp  $0x3e8,%ebp    ; c < 1000?
 ╰ 0x00007f964d0894c6: jl   0x00007f964d0893d2 ;

一切都檢查 OK。

4. 觀察結(jié)果

當(dāng)鎖粗化在整個(gè)循環(huán)中不起作用時(shí),一旦中間看起來好像存在 N 個(gè)相鄰的加鎖解鎖操作,另一種循環(huán)優(yōu)化——循環(huán)展開會(huì)提供常規(guī)鎖粗化。這將提高性能,并有助于限制粗化的范圍,以避免長循環(huán)過度粗化。

總結(jié)

以上所述是小編給大家介紹的Java 鎖粗化與循環(huán)問題,希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!

如果你覺得本文對(duì)你有幫助,歡迎轉(zhuǎn)載,煩請(qǐng)注明出處,謝謝!

相關(guān)文章

  • springboot @ConfigurationProperties和@PropertySource的區(qū)別

    springboot @ConfigurationProperties和@PropertySource的區(qū)別

    這篇文章主要介紹了springboot @ConfigurationProperties和@PropertySource的區(qū)別,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • 如何使用Java 8 中的 Stream 遍歷樹形結(jié)構(gòu)

    如何使用Java 8 中的 Stream 遍歷樹形結(jié)構(gòu)

    這篇文章主要介紹了使用Java 8中的Stream遍歷樹形結(jié)構(gòu),我們可以使用Java8中的Stream流一次性把數(shù)據(jù)查出來,然后通過流式處理,我們一起來看看,代碼實(shí)現(xiàn)為了實(shí)現(xiàn)簡單,就模擬查看數(shù)據(jù)庫所有數(shù)據(jù)到List里面,需要的朋友可以參考下
    2023-08-08
  • Java中的String、StringBuilder、StringBuffer三者的區(qū)別詳解

    Java中的String、StringBuilder、StringBuffer三者的區(qū)別詳解

    這篇文章主要介紹了Java中的String、StringBuilder、StringBuffer三者的區(qū)別詳解,就是String,StringBuilder以及StringBuffer這三個(gè)類之間有什么區(qū)別呢,自己從網(wǎng)上搜索了一些資料,有所了解了之后在這里整理一下,便于大家觀看,需要的朋友可以參考下
    2023-12-12
  • java 使用過濾器實(shí)現(xiàn)登錄攔截處理

    java 使用過濾器實(shí)現(xiàn)登錄攔截處理

    這篇文章主要介紹了java 使用過濾器實(shí)現(xiàn)登錄攔截處理方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • MyBatisPlus 一對(duì)多、多對(duì)一、多對(duì)多的完美解決方案

    MyBatisPlus 一對(duì)多、多對(duì)一、多對(duì)多的完美解決方案

    這篇文章主要介紹了MyBatisPlus 一對(duì)多、多對(duì)一、多對(duì)多的完美解決方案,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-11-11
  • 一文詳解Spring Security的基本用法

    一文詳解Spring Security的基本用法

    Spring Security是一個(gè)功能強(qiáng)大且高度可定制的身份驗(yàn)證和訪問控制框架, 提供了完善的認(rèn)證機(jī)制和方法級(jí)的授權(quán)功能。本文將通過一個(gè)簡單的案例了解一下Spring Security的基本用法,需要的可以參考一下
    2022-05-05
  • Java網(wǎng)絡(luò)編程之簡易聊天室的實(shí)現(xiàn)

    Java網(wǎng)絡(luò)編程之簡易聊天室的實(shí)現(xiàn)

    這篇文章主要為大家詳細(xì)介紹了如何利用Java語言實(shí)現(xiàn)一個(gè)簡易聊天室功能,可以實(shí)現(xiàn)運(yùn)行客戶端和連接服務(wù)器,文中的示例代碼講解詳細(xì),需要的可以了解一下
    2022-10-10
  • spring boot發(fā)簡單文本郵件案例

    spring boot發(fā)簡單文本郵件案例

    這篇文章主要介紹了spring boot發(fā)簡單文本郵件案例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-10-10
  • 詳解Java并發(fā)包中線程池ThreadPoolExecutor

    詳解Java并發(fā)包中線程池ThreadPoolExecutor

    ThreadPoolExecutor是Java語言對(duì)于線程池的實(shí)現(xiàn)。線程池技術(shù)使線程在使用完畢后不回收而是重復(fù)利用。如果線程能夠復(fù)用,那么我們就可以使用固定數(shù)量的線程來解決并發(fā)問題,這樣一來不僅節(jié)約了系統(tǒng)資源,而且也會(huì)減少線程上下文切換的開銷
    2021-06-06
  • SpringBoot中@Pattern注解對(duì)時(shí)間格式校驗(yàn)方式

    SpringBoot中@Pattern注解對(duì)時(shí)間格式校驗(yàn)方式

    這篇文章主要介紹了SpringBoot中@Pattern注解對(duì)時(shí)間格式校驗(yàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09

最新評(píng)論