<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Go垃圾回收系列之九:內(nèi)存屏障

          共 2555字,需瀏覽 6分鐘

           ·

          2021-03-20 10:32

          并發(fā)標(biāo)記由于標(biāo)記協(xié)程與用戶協(xié)程共同工作,帶來了很多難題。如果說輔助標(biāo)記是為了保證垃圾回收能夠正常的結(jié)束與循環(huán)、那么本小節(jié)將解決更棘手的問題:準(zhǔn)確性。如下所示,假設(shè)在垃圾收集已經(jīng)掃描完根(此時根對象都為黑色),并繼續(xù)掃描期間,白色對象Z正被一個灰色對象引用。但是此時,工作協(xié)程在執(zhí)行過程中,讓黑色的根對象指向了白色的對象Z。由于黑色的對象不會被掃描,這將導(dǎo)致白色對象Z被視為垃圾對象,最終被回收。這就導(dǎo)致了致命的錯誤。

          那么是不是黑色對象一定不能夠指向白色對象呢?其實也不一定。如下所示,即便是黑色對象引用了白色對象,但只要是白色對象有一條路勁被灰色對象引用了,那么此白色對象也一定能夠被掃描到。


          這其實引出了并發(fā)標(biāo)記要保證準(zhǔn)確性,即垃圾收集器能夠掃描到所有活著的對象,需要遵守的原則。

          這就是強弱三色不變性。

          強三色不變性 指的是 所有白色的對象都不能夠被黑色的對象引用,這是一種比較嚴(yán)格的保證。與之對應(yīng)的弱三色不變性。

          弱三色不變性允許白色對象被黑色對象引用,但是白色對象指向有一條路徑,最終是被黑色對象引用的,這保證了該對象最終能夠被掃描到。

          在并發(fā)標(biāo)記寫入和刪除對象時,可能會破壞三色不變性,因此必須要有一種機制能夠維護三色不變性,這就是屏障策略。屏障策略的原則是保護活著的對象在寫入或者能夠刪除對象的時候?qū)⑦m當(dāng)?shù)膶ο笞優(yōu)榛疑珜崿F(xiàn)的。例如上例中,如果能夠在對象寫入時, 將Z對象設(shè)置為灰色,那么Z對象將最終被掃描到。


          上圖提到的這種簡單的屏障技術(shù)是Dijkstra[1976, 1978]風(fēng)格的插入屏障,其實現(xiàn)形式如下,如果目標(biāo)對象src為黑色,則將新引用的對象標(biāo)記為灰色。

          Write(src, i, ref):

          src[i] ← ref

          ??if isBlack(src)

          ????shade(ref)

          又如另一種常見的策略是在刪除引用的時候做文章,Yuasa [1990]刪除寫屏障一旦取消了原引用后,就立即將原引用標(biāo)記為灰色。


          這樣即便沒有寫屏障,插入時也不會破壞三色不變性,如下所示。但是Z對象可能是垃圾對象。


          插入屏障與刪除屏障通過在寫入和刪除時重新標(biāo)記顏色保證了三色不變性,解決了并發(fā)標(biāo)記期間的準(zhǔn)確性問題,但是他們都存儲浮動垃圾的問題。插入屏障在刪除引用的時候,可能一個已經(jīng)變成垃圾的對象仍然被標(biāo)記了。而刪除屏障在刪除了刪除引用的時候可能把一個垃圾對象標(biāo)記為了灰色。這叫做垃圾回收的精度問題但是不會影響其準(zhǔn)確性。因為浮動垃圾會在下一次垃圾回收中被收集。

          插入屏障與刪除屏障獨立存在并能夠良好工作的前提是對于并發(fā)標(biāo)記期間所有的寫入都應(yīng)用了屏障技術(shù),但現(xiàn)實情況不會如此。大多數(shù)垃圾回收語言不會對棧上的操作或寄存器上的操作進(jìn)行相同的屏障技術(shù),因為棧上操作是最頻繁的,每個寫入或刪除操作應(yīng)用屏障技術(shù)會大大減慢程序的速度。所以在Go1.9之前,盡管應(yīng)用了插入屏障,但是仍然需要在標(biāo)記終止期間STW階段重新重新掃描根對象,來保證三色標(biāo)記的一致性。而Go1.9之后,使用了混合寫屏障技術(shù),結(jié)合了類似Dijkstra 與 Yuasa 兩種風(fēng)格。為了了解為什么需要混合寫屏障技術(shù),首先來看一看單純的插入屏障和刪除屏障在現(xiàn)實中的困境。

          假設(shè)棧上一開始的情況如下圖,棧上變量P指向了堆區(qū)內(nèi)存。假設(shè)現(xiàn)在垃圾回收掃描完了根對象,這時old變量是不會被掃描的,同時進(jìn)入到了標(biāo)記階段

          在并發(fā)標(biāo)記階段,old對象引用了p.x,但是賦值給棧上的變量不會經(jīng)過寫屏障。如果下一步,p.x引用了一個新的內(nèi)存對象k,并把k標(biāo)記為灰色,但是并不把原始的對象標(biāo)記為灰色,這時,原始的對象雖然被棧上的對象old標(biāo)記了,但是卻無法被掃描到,因此會出現(xiàn)致命問題。所以必須在p.x=&k應(yīng)用刪除屏障,在取消引用時,將p.x的原值標(biāo)記為灰色。

          如果只有刪除屏障而沒有寫屏障,也會面臨問題:

          如下,假設(shè)一開始根對象還沒開始掃描,全為白色對象時的情形,棧上變量p引用堆區(qū)o,棧上變量a引用堆區(qū)k, 在并發(fā)標(biāo)記期間,假設(shè)開始掃描過了變量p還沒開始變量a時的情形如下。

          此時,工作協(xié)程將變量a置為nil,p.x = &k將對象p指向了k。此時如果只有刪除屏障而不啟用寫屏障(不標(biāo)記新的值k),會看到當(dāng)前直接違背三色不變性,讓黑色的對象引用了白色的對象。這會導(dǎo)致k無法被標(biāo)記。

          因此,要想在標(biāo)記終止階段不用重新掃描根對象,需要使用寫屏障與刪除屏障混合的屏障技術(shù),其偽代碼如下所示:

          writePointer(slot, ptr):
          shade(*slot)
          shade(ptr)
          *slot = ptr

          在Go語言中,混合屏障依賴于編譯時與運行時的共同努力。在標(biāo)記準(zhǔn)備階段的STW階段會打開寫屏障,具體是讓全局變量writeBarrier.enabled設(shè)置為true.

          var writeBarrier struct {
          enabled bool
          pad [3]byte
          needed bool
          cgo bool
          alignme uint64
          }

          編譯器會在所有堆寫入或刪除操作前判斷當(dāng)前是否為垃圾回收標(biāo)記階段,如果是則會執(zhí)行對應(yīng)的混合寫屏障標(biāo)記對象。在匯編代碼中表示如下,其中g(shù)cWriteBarrier是與平臺相關(guān)的操作,執(zhí)行標(biāo)記邏輯。

          CMPL	runtime.writeBarrier(SB), $0
          CALL runtime.gcWriteBarrier(SB)

          Go語言構(gòu)建了一個寫屏障指針的緩存池,gcWriteBarrier首先所有被標(biāo)記的指針會放入到緩存池中,并在容量滿之后,一次性全部刷新到掃描工作池中。



          推薦閱讀


          福利

          我為大家整理了一份從入門到進(jìn)階的Go學(xué)習(xí)資料禮包,包含學(xué)習(xí)建議:入門看什么,進(jìn)階看什么。關(guān)注公眾號 「polarisxu」,回復(fù) ebook 獲取;還可以回復(fù)「進(jìn)群」,和數(shù)萬 Gopher 交流學(xué)習(xí)。

          瀏覽 69
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  含羞草一区二区三区无码观看 | 黄色小视频在线免费看 | 中国精品一级片内射一级片 | 蜜臀AV在线播放 | 青娱乐欧美国产亚洲自拍 |