<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垃圾回收系列之七:標(biāo)記終止與調(diào)步算法

          共 2475字,需瀏覽 5分鐘

           ·

          2021-03-18 22:18

          標(biāo)記終止階段

          當(dāng)完成并發(fā)標(biāo)記階段所有灰色對象的掃描和標(biāo)記,則進(jìn)入到標(biāo)記終止階段。標(biāo)記終止階段會再次進(jìn)入STW,標(biāo)記終止階段主要完成一些指標(biāo)例如用時的統(tǒng)計(jì)、統(tǒng)計(jì)強(qiáng)制開始GC的次數(shù)、更新下一次觸發(fā)gc需要達(dá)到的目標(biāo)、關(guān)閉寫屏障、并喚醒后臺清掃的協(xié)程,開始下一階段的清掃工作。

          在標(biāo)記終止階段重要的任務(wù)是計(jì)算下一次觸發(fā)垃圾回收時需要達(dá)到的堆目標(biāo),這叫做垃圾回收的調(diào)步算法。

          調(diào)步算法是Go1.5提出的算法,由于Go1.5開始使用并發(fā)的三色標(biāo)記,因此,當(dāng)開始進(jìn)行GC到GC結(jié)束的過程中,用戶協(xié)程也可能在分配大量的內(nèi)存。所以在GC的過程中,內(nèi)存的大小實(shí)際上超過了我們設(shè)定的觸發(fā)GC的目標(biāo)。為了解決這樣的問題,需要對程序進(jìn)行估計(jì),從而在目標(biāo)內(nèi)存之前就啟動GC、并預(yù)計(jì)在GC結(jié)束之后,程序的內(nèi)存大小剛好在目標(biāo)內(nèi)存附近。

          因此,調(diào)步算法最重要的任務(wù)是估計(jì)出最佳的下一次觸發(fā)GC的時機(jī),而這依賴于本次GC階段差額最終GC完成后的內(nèi)存與目標(biāo)內(nèi)存之前的差距。當(dāng)GC完成后的內(nèi)存遠(yuǎn)小于目標(biāo)內(nèi)存,意味著我們觸發(fā)GC的時間過早。如果GC完成后的內(nèi)存遠(yuǎn)大于目標(biāo)內(nèi)存,意味著觸發(fā)GC的時間太遲。

          因此調(diào)度算法 的第一個目標(biāo)是min(|目前內(nèi)存-本次GC完成標(biāo)記后的內(nèi)存|),除此之外,調(diào)步算法還有第二個目標(biāo),即預(yù)計(jì)執(zhí)行標(biāo)記的CPU暫用率接近25%。結(jié)合之前提到的25%的后臺標(biāo)記協(xié)程,這個目標(biāo)是滿足的,正常情況下,只會有25%的CPU去執(zhí)行后臺標(biāo)記任務(wù)。但是當(dāng)用戶工作協(xié)程執(zhí)行了“輔助標(biāo)記”

          (下一篇文章介紹),這一目標(biāo)將不再成立。當(dāng)用戶協(xié)程執(zhí)行了過多的輔助標(biāo)記,這將導(dǎo)致GC標(biāo)記完成后的內(nèi)存偏小,因?yàn)橛脩魠f(xié)程本來應(yīng)該分配內(nèi)存的時間用來了執(zhí)行輔助標(biāo)記。

          算法將首先計(jì)算目標(biāo)內(nèi)存與實(shí)際內(nèi)存的偏差,這是通過計(jì)算

          偏差率 =  (目標(biāo)增長率 - 觸發(fā)增率) - (實(shí)際增長率 - 觸發(fā)率)

          來實(shí)現(xiàn)的。這其實(shí)是偏差=(目標(biāo)內(nèi)存 - 觸發(fā)GC時的內(nèi)存) - (GC標(biāo)記完成后的內(nèi)存- 觸發(fā)GC時的內(nèi)存) 的變形。

          為了修復(fù)輔助標(biāo)記帶來的偏差,計(jì)算了輔助標(biāo)記所用的時間,從而調(diào)整了(GC標(biāo)記完成后的內(nèi)存- 觸發(fā)GC時的內(nèi)存) 的大小。因此最終的偏差會調(diào)整為:

          偏差率   =  (目標(biāo)增長率 - 觸發(fā)率) -  調(diào)整率  *(實(shí)際增長率 - 觸發(fā)率)

          實(shí)際代碼如下:

          func (c *gcControllerState) endCycle() float64 {
          utilization := gcBackgroundUtilization
          if assistDuration > 0 {
          utilization += float64(c.assistTime) / float64(assistDuration*int64(gomaxprocs))
          }
          triggerError := goalGrowthRatio - memstats.triggerRatio - utilization/gcGoalUtilization*(actualGrowthRatio-memstats.triggerRatio)
          triggerRatio := memstats.triggerRatio + triggerGain*triggerError
          }

          從公式中可以看出,實(shí)際增長率 和  輔助標(biāo)記的時間都會影響最終的偏差。當(dāng)調(diào)整后估計(jì)的實(shí)際內(nèi)存越偏離于實(shí)際內(nèi)存時,偏差率越大。這時,會調(diào)整下一次GC時的觸發(fā)率,調(diào)整時,采取了漸進(jìn)調(diào)整,每次只調(diào)整偏差的一半。下一次GC時的觸發(fā)率的公式如下:

          下次GC觸發(fā)率 = 上次GC觸發(fā)率  + 1/2 * 偏差率

          計(jì)算完GC觸發(fā)率之后,最終需要計(jì)算出實(shí)際的GC觸發(fā)大小。這是在標(biāo)記終止階段gcSetTriggerRatio函數(shù)中完成的。目標(biāo)內(nèi)存的大小計(jì)算為:

          goal := ^uint64(0)

          if gcpercent >= 0 {
          goal = memstats.heap_marked + memstats.heap_marked*uint64(gcpercent)/100
          }

          goal為下次GC完成后期望的目標(biāo)內(nèi)存,其取決于本次GC中掃描后內(nèi)存的大小以及gcpercent的大小。gcpercent是可以由用戶動態(tài)設(shè)置的。調(diào)用debug標(biāo)準(zhǔn)庫的SetGCPercent函數(shù),可以修改gcpercent的大小。

          func SetGCPercent(percent int) int

          gcpercent默認(rèn)為100,代表目標(biāo)內(nèi)存是前一此GC標(biāo)記內(nèi)存的一倍。當(dāng)修改gcpercent小于0,將禁用Go的垃圾回收。另外,也可以在編譯或運(yùn)行時添加"GOGC"環(huán)境變量的方式修改gcpercent大小。核心邏輯是在程序初始化時調(diào)用readgogc()實(shí)現(xiàn)的。例如GOGC=off ./main 將關(guān)閉程序的GC回收。

          func readgogc() int32 {
          p := gogetenv("GOGC")
          if p == "off" {
          return -1
          }

          if n, ok := atoi32(p); ok {

          return n

          }
          return 100
          }

          當(dāng)明確了目標(biāo)內(nèi)存大小,觸發(fā)內(nèi)存的大小可以簡單定義為觸發(fā)內(nèi)存的大小 = 觸發(fā)率 * 目標(biāo)內(nèi)存大小,但觸發(fā)率不能超過0.95,也不能夠小于0.6。因此最終觸發(fā)內(nèi)存大小在0.6*目標(biāo)內(nèi)存大小—0.95*目標(biāo)內(nèi)存大小之間。



          推薦閱讀


          福利

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

          瀏覽 7
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <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>
                  二区三区免费视频 | 三级三级久久三级久久18 | 久久三级久久三级久久三级 | 97视频在线 | 三级小视频在线观看 |