日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
深入理解Go-垃圾回收機(jī)制

Go的GC自打出生的時(shí)候就開(kāi)始被人詬病,但是在引入v1.5的三色標(biāo)記和v1.8的混合寫屏障后,正常的GC已經(jīng)縮短到10us左右,已經(jīng)變得非常優(yōu)秀,了不起了,我們接下來(lái)探索一下Go的GC的原理吧

網(wǎng)站建設(shè)哪家好,找成都創(chuàng)新互聯(lián)!專注于網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站建設(shè)、微信開(kāi)發(fā)、微信小程序定制開(kāi)發(fā)、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了江油免費(fèi)建站歡迎大家使用!

三色標(biāo)記原理

我們首先看一張圖,大概就會(huì)對(duì) 三色標(biāo)記法 有一個(gè)大致的了解:

原理:

  1.  首先把所有的對(duì)象都放到白色的集合中
  2.  從根節(jié)點(diǎn)開(kāi)始遍歷對(duì)象,遍歷到的白色對(duì)象從白色集合中放到灰色集合中
  3.  遍歷灰色集合中的對(duì)象,把灰色對(duì)象引用的白色集合的對(duì)象放入到灰色集合中,同時(shí)把遍歷過(guò)的灰色集合中的對(duì)象放到黑色的集合中
  4.  循環(huán)步驟3,知道灰色集合中沒(méi)有對(duì)象
  5.  步驟4結(jié)束后,白色集合中的對(duì)象就是不可達(dá)對(duì)象,也就是垃圾,進(jìn)行回收

寫屏障

Go在進(jìn)行三色標(biāo)記的時(shí)候并沒(méi)有STW,也就是說(shuō),此時(shí)的對(duì)象還是可以進(jìn)行修改

那么我們考慮一下,下面的情況

我們?cè)谶M(jìn)行三色標(biāo)記中掃描灰色集合中,掃描到了對(duì)象A,并標(biāo)記了對(duì)象A的所有引用,這時(shí)候,開(kāi)始掃描對(duì)象D的引用,而此時(shí),另一個(gè)goroutine修改了D->E的引用,變成了如下圖所示

這樣會(huì)不會(huì)導(dǎo)致E對(duì)象就掃描不到了,而被誤認(rèn)為 為白色對(duì)象,也就是垃圾

寫屏障就是為了解決這樣的問(wèn)題,引入寫屏障后,在上述步驟后,E會(huì)被認(rèn)為是存活的,即使后面E被A對(duì)象拋棄,E會(huì)被在下一輪的GC中進(jìn)行回收,這一輪GC中是不會(huì)對(duì)對(duì)象E進(jìn)行回收的

Go1.9中開(kāi)始啟用了混合寫屏障,偽代碼如下

 
 
 
 
  1. writePointer(slot, ptr):  
  2.     shade(*slot)  
  3.     if any stack is grey:  
  4.         shade(ptr)  
  5.     *slot = ptr 

混合寫屏障會(huì)同時(shí)標(biāo)記指針寫入目標(biāo)的"原指針"和“新指針".

標(biāo)記原指針的原因是, 其他運(yùn)行中的線程有可能會(huì)同時(shí)把這個(gè)指針的值復(fù)制到寄存器或者棧上的本地變量

因?yàn)閺?fù)制指針到寄存器或者棧上的本地變量不會(huì)經(jīng)過(guò)寫屏障, 所以有可能會(huì)導(dǎo)致指針不被標(biāo)記, 試想下面的情況:

 
 
 
 
  1. [go] b = obj  
  2. [go] oldx = nil  
  3. [gc] scan oldx...  
  4. [go] oldx = b.x // 復(fù)制b.x到本地變量, 不進(jìn)過(guò)寫屏障  
  5. [go] b.x = ptr // 寫屏障應(yīng)該標(biāo)記b.x的原值  
  6. [gc] scan b...  
  7. 如果寫屏障不標(biāo)記原值, 那么oldx就不會(huì)被掃描到. 

標(biāo)記新指針的原因是, 其他運(yùn)行中的線程有可能會(huì)轉(zhuǎn)移指針的位置, 試想下面的情況:

 
 
 
 
  1. [go] a = ptr  
  2. [go] b = obj  
  3. [gc] scan b...  
  4. [go] b.x = a // 寫屏障應(yīng)該標(biāo)記b.x的新值  
  5. [go] a = nil  
  6. [gc] scan a...  
  7. 如果寫屏障不標(biāo)記新值, 那么ptr就不會(huì)被掃描到. 

混合寫屏障可以讓GC在并行標(biāo)記結(jié)束后不需要重新掃描各個(gè)G的堆棧, 可以減少M(fèi)ark Termination中的STW時(shí)間

除了寫屏障外, 在GC的過(guò)程中所有新分配的對(duì)象都會(huì)立刻變?yōu)楹谏? 在上面的mallocgc函數(shù)中可以看到

回收流程

GO的GC是并行GC, 也就是GC的大部分處理和普通的go代碼是同時(shí)運(yùn)行的, 這讓GO的GC流程比較復(fù)雜.

首先GC有四個(gè)階段, 它們分別是:

  •  Sweep Termination: 對(duì)未清掃的span進(jìn)行清掃, 只有上一輪的GC的清掃工作完成才可以開(kāi)始新一輪的GC
  •  Mark: 掃描所有根對(duì)象, 和根對(duì)象可以到達(dá)的所有對(duì)象, 標(biāo)記它們不被回收
  •  Mark Termination: 完成標(biāo)記工作, 重新掃描部分根對(duì)象(要求STW)
  •  Sweep: 按標(biāo)記結(jié)果清掃span

下圖是比較完整的GC流程, 并按顏色對(duì)這四個(gè)階段進(jìn)行了分類:

在GC過(guò)程中會(huì)有兩種后臺(tái)任務(wù)(G), 一種是標(biāo)記用的后臺(tái)任務(wù), 一種是清掃用的后臺(tái)任務(wù).

標(biāo)記用的后臺(tái)任務(wù)會(huì)在需要時(shí)啟動(dòng), 可以同時(shí)工作的后臺(tái)任務(wù)數(shù)量大約是P的數(shù)量的25%, 也就是go所講的讓25%的cpu用在GC上的根據(jù).

清掃用的后臺(tái)任務(wù)在程序啟動(dòng)時(shí)會(huì)啟動(dòng)一個(gè), 進(jìn)入清掃階段時(shí)喚醒.

目前整個(gè)GC流程會(huì)進(jìn)行兩次STW(Stop The World), 第一次是Mark階段的開(kāi)始, 第二次是Mark Termination階段.

第一次STW會(huì)準(zhǔn)備根對(duì)象的掃描, 啟動(dòng)寫屏障(Write Barrier)和輔助GC(mutator assist).

第二次STW會(huì)重新掃描部分根對(duì)象, 禁用寫屏障(Write Barrier)和輔助GC(mutator assist).

需要注意的是, 不是所有根對(duì)象的掃描都需要STW, 例如掃描棧上的對(duì)象只需要停止擁有該棧的G.

寫屏障的實(shí)現(xiàn)使用了Hybrid Write Barrier, 大幅減少了第二次STW的時(shí)間.

源碼分析

gcStart

 
 
 
 
  1. func gcStart(mode gcMode, trigger gcTrigger) {  
  2.     // Since this is called from malloc and malloc is called in  
  3.     // the guts of a number of libraries that might be holding  
  4.     // locks, don't attempt to start GC in non-preemptible or  
  5.     // potentially unstable situations.  
  6.     // 判斷當(dāng)前g是否可以搶占,不可搶占時(shí)不觸發(fā)GC  
  7.     mp := acquirem()  
  8.     if gp := getg(); gp == mp.g0 || mp.locks > 1 || mp.preemptoff != "" {  
  9.         releasem(mp)  
  10.         return  
  11.     }  
  12.     releasem(mp)  
  13.     mp = nil  
  14.     // Pick up the remaining unswept/not being swept spans concurrently  
  15.     //  
  16.     // This shouldn't happen if we're being invoked in background  
  17.     // mode since proportional sweep should have just finished  
  18.     // sweeping everything, but rounding errors, etc, may leave a  
  19.     // few spans unswept. In forced mode, this is necessary since  
  20.     // GC can be forced at any point in the sweeping cycle.  
  21.     //  
  22.     // We check the transition condition continuously here in case  
  23.     // this G gets delayed in to the next GC cycle.  
  24.     // 清掃 殘留的未清掃的垃圾  
  25.     for trigger.test() && gosweepone() != ^uintptr(0) {  
  26.         sweep.nbgsweep++  
  27.     }  
  28.     // Perform GC initialization and the sweep termination  
  29.     // transition.  
  30.     semacquire(&work.startSema)  
  31.     // Re-check transition condition under transition lock.  
  32.     // 判斷gcTrriger的條件是否成立  
  33.     if !trigger.test() {  
  34.         semrelease(&work.startSema)  
  35.         return  
  36.     }  
  37.     // For stats, check if this GC was forced by the user  
  38.     // 判斷并記錄GC是否被強(qiáng)制執(zhí)行的,runtime.GC()可以被用戶調(diào)用并強(qiáng)制執(zhí)行  
  39.     work.userForced = trigger.kind == gcTriggerAlways || trigger.kind == gcTriggerCycle  
  40.     // In gcstoptheworld debug mode, upgrade the mode accordingly.  
  41.     // We do this after re-checking the transition condition so  
  42.     // that multiple goroutines that detect the heap trigger don't  
  43.     // start multiple STW GCs.  
  44.     // 設(shè)置gc的mode  
  45.     if mode == gcBackgroundMode {  
  46.         if debug.gcstoptheworld == 1 {  
  47.             mode = gcForceMode  
  48.         } else if debug.gcstoptheworld == 2 {  
  49.             mode = gcForceBlockMode  
  50.         }  
  51.     }  
  52.     // Ok, we're doing it! Stop everybody else  
  53.     semacquire(&worldsema) 
  54.     if trace.enabled {  
  55.         traceGCStart()  
  56.     }  
  57.     // 啟動(dòng)后臺(tái)標(biāo)記任務(wù)  
  58.     if mode == gcBackgroundMode {  
  59.         gcBgMarkStartWorkers()  
  60.     }  
  61.     // 重置gc 標(biāo)記相關(guān)的狀態(tài)  
  62.     gcResetMarkState()  
  63.     work.stwprocs, work.maxprocs = gomaxprocs, gomaxprocs  
  64.     if work.stwprocs > ncpu {  
  65.         // This is used to compute CPU time of the STW phases,  
  66.         // so it can't be more than ncpu, even if GOMAXPROCS is.  
  67.         work.stwprocs = ncpu  
  68.     }  
  69.     work.heap0 = atomic.Load64(&memstats.heap_live)  
  70.     work.pauseNS = 0  
  71.     work.mode = mode  
  72.     now := nanotime()  
  73.     work.tSweepTerm = now  
  74.     work.pauseStart = now  
  75.     if trace.enabled {  
  76.         traceGCSTWStart(1)  
  77.     }  
  78.     // STW,停止世界  
  79.     systemstack(stopTheWorldWithSema)  
  80.     // Finish sweep before we start concurrent scan.  
  81.     // 先清掃上一輪的垃圾,確保上輪GC完成  
  82.     systemstack(func() {  
  83.         finishsweep_m()  
  84.     })  
  85.     // clearpools before we start the GC. If we wait they memory will not be  
  86.     // reclaimed until the next GC cycle.  
  87.     // 清理 sync.pool sched.sudogcache、sched.deferpool,這里不展開(kāi),sync.pool已經(jīng)說(shuō)了,剩余的后面的文章會(huì)涉及  
  88.     clearpools()  
  89.     // 增加GC技術(shù)  
  90.     work.cycles++  
  91.     if mode == gcBackgroundMode { // Do as much work concurrently as possible  
  92.         gcController.startCycle()  
  93.         work.heapGoal = memstats.next_gc  
  94.         // Enter concurrent mark phase and enable  
  95.         // write barriers. 
  96.         //  
  97.         // Because the world is stopped, all Ps will  
  98.         // observe that write barriers are enabled by  
  99.         // the time we start the world and begin  
  100.         // scanning.  
  101.         //  
  102.         // Write barriers must be enabled before assists are  
  103.         // enabled because they must be enabled before  
  104.         // any non-leaf heap objects are marked. Since  
  105.         // allocations are blocked until assists can  
  106.         // happen, we want enable assists as early as  
  107.         // possible.  
  108.         // 設(shè)置GC的狀態(tài)為 gcMark  
  109.         setGCPhase(_GCmark)  
  110.         // 更新 bgmark 的狀態(tài)  
  111.         gcBgMarkPrepare() // Must happen before assist enable.  
  112.         // 計(jì)算并排隊(duì)root 掃描任務(wù),并初始化相關(guān)掃描任務(wù)狀態(tài)  
  113.         gcMarkRootPrepare()  
  114.         // Mark all active tinyalloc blocks. Since we're  
  115.         // allocating from these, they need to be black like  
  116.         // other allocations. The alternative is to blacken  
  117.         // the tiny block on every allocation from it, which  
  118.         // would slow down the tiny allocator.  
  119.         // 標(biāo)記 tiny 對(duì)象  
  120.         gcMarkTinyAllocs()  
  121.         // At this point all Ps have enabled the write  
  122.         // barrier, thus maintaining the no white to  
  123.         // black invariant. Enable mutator assists to  
  124.         // put back-pressure on fast allocating  
  125.         // mutators.  
  126.         // 設(shè)置 gcBlackenEnabled 為 1,啟用寫屏障  
  127.         atomic.Store(&gcBlackenEnabled, 1)  
  128.         // Assists and workers can start the moment we start  
  129.         // the world.  
  130.         gcController.markStartTime = now  
  131.         // Concurrent mark.  
  132.         systemstack(func() {  
  133.             now = startTheWorldWithSema(trace.enabled)  
  134.         })  
  135.         work.pauseNS += now - work.pauseStart  
  136.         work.tMark = now  
  137.     } else {  
  138.         // 非并行模式  
  139.         // 記錄完成標(biāo)記階段的開(kāi)始時(shí)間  
  140.         if trace.enabled {  
  141.             // Switch to mark termination STW.  
  142.             traceGCSTWDone()  
  143.             traceGCSTWStart(0)  
  144.         }  
  145.         t := nanotime()  
  146.         work.tMark, work.tMarkTerm = t, t  
  147.         workwork.heapGoal = work.heap0  
  148.         // Perform mark termination. This will restart the world.  
  149.         // stw,進(jìn)行標(biāo)記,清掃并start the world 
  150.          gcMarkTermination(memstats.triggerRatio)  
  151.     }  
  152.     semrelease(&work.startSema)  

gcBgMarkStartWorkers

這個(gè)函數(shù)準(zhǔn)備一些 執(zhí)行bg mark工作的goroutine,但是這些goroutine并不是立即工作的,而是到等到GC的狀態(tài)被標(biāo)記為gcMark 才開(kāi)始工作,見(jiàn)上個(gè)函數(shù)的119行

 
 
 
 
  1. func gcBgMarkStartWorkers() {  
  2.     // Background marking is performed by per-P G's. Ensure that  
  3.     // each P has a background GC G.  
  4.     for _, p := range allp {  
  5.         if p.gcBgMarkWorker == 0 {  
  6.             go gcBgMarkWorker(p)  
  7.             // 等待gcBgMarkWorker goroutine 的 bgMarkReady信號(hào)再繼續(xù)  
  8.             notetsleepg(&work.bgMarkReady, -1)  
  9.             noteclear(&work.bgMarkReady)  
  10.         }  
  11.     }  

gcBgMarkWorker

后臺(tái)標(biāo)記任務(wù)的函數(shù)

 
 
 
 
  1. func gcBgMarkWorker(_p_ *p) {  
  2.     gp := getg()  
  3.     // 用于休眠結(jié)束后重新獲取p和m  
  4.     type parkInfo struct {  
  5.         m      muintptr // Release this m on park.  
  6.         attach puintptr // If non-nil, attach to this p on park.  
  7.     }  
  8.     // We pass park to a gopark unlock function, so it can't be on  
  9.     // the stack (see gopark). Prevent deadlock from recursively  
  10.     // starting GC by disabling preemption.  
  11.     gp.m.preemptoff = "GC worker init"  
  12.     park := new(parkInfo)  
  13.     gp.m.preemptoff = ""  
  14.     // 設(shè)置park的m和p的信息,留著后面?zhèn)鹘ogopark,在被gcController.findRunnable喚醒的時(shí)候,便于找回  
  15.     park.m.set(acquirem())  
  16.     park.attach.set(_p_)  
  17.     // Inform gcBgMarkStartWorkers that this worker is ready.  
  18.     // After this point, the background mark worker is scheduled  
  19.     // cooperatively by gcController.findRunnable. Hence, it must  
  20.     // never be preempted, as this would put it into _Grunnable  
  21.     // and put it on a run queue. Instead, when the preempt flag  
  22.     // is set, this puts itself into _Gwaiting to be woken up by  
  23.     // gcController.findRunnable at the appropriate time.  
  24.     // 讓gcBgMarkStartWorkers notetsleepg停止等待并繼續(xù)及退出  
  25.     notewakeup(&work.bgMarkReady)  
  26.     for {  
  27.         // Go to sleep until woken by gcController.findRunnable.  
  28.         // We can't releasem yet since even the call to gopark  
  29.         // may be preempted.  
  30.         // 讓g進(jìn)入休眠  
  31.         gopark(func(g *g, parkp unsafe.Pointer) bool {  
  32.             park := (*parkInfo)(parkp)  
  33.             // The worker G is no longer running, so it's  
  34.             // now safe to allow preemption.  
  35.             // 釋放當(dāng)前搶占的m  
  36.             releasem(park.m.ptr())  
  37.             // If the worker isn't attached to its P,  
  38.             // attach now. During initialization and after  
  39.             // a phase change, the worker may have been  
  40.             // running on a different P. As soon as we  
  41.             // attach, the owner P may schedule the  
  42.             // worker, so this must be done after the G is  
  43.             // stopped.  
  44.             // 設(shè)置關(guān)聯(lián)p,上面已經(jīng)設(shè)置過(guò)了  
  45.             if park.attach != 0 {  
  46.                 p := park.attach.ptr()  
  47.                 park.attach.set(nil)  
  48.                 // cas the worker because we may be  
  49.                 // racing with a new worker starting  
  50.                 // on this P.  
  51.                 if !p.gcBgMarkWorker.cas(0, guintptr(unsafe.Pointer(g))) {  
  52.                     // The P got a new worker.  
  53.                     // Exit this worker.  
  54.                     return false  
  55.                 }  
  56.             }  
  57.             return true  
  58.         }, unsafe.Pointer(park), waitReasonGCWorkerIdle, traceEvGoBlock, 0)  
  59.         // Loop until the P dies and disassociates this  
  60.         // worker (the P may later be reused, in which case  
  61.         // it will get a new worker) or we failed to associate.  
  62.         // 檢查P的gcBgMarkWorker是否和當(dāng)前的G一致, 不一致時(shí)結(jié)束當(dāng)前的任務(wù)  
  63.         if _p_.gcBgMarkWorker.ptr() != gp {  
  64.             break  
  65.         }  
  66.         // Disable preemption so we can use the gcw. If the  
  67.         // scheduler wants to preempt us, we'll stop draining,  
  68.         // dispose the gcw, and then preempt.  
  69.         // gopark第一個(gè)函數(shù)中釋放了m,這里再搶占回來(lái)  
  70.         park.m.set(acquirem())  
  71.         if gcBlackenEnabled == 0 {  
  72.             throw("gcBgMarkWorker: blackening not enabled")  
  73.         }  
  74.         startTime := nanotime()  
  75.         // 設(shè)置gcmark的開(kāi)始時(shí)間  
  76.         _p_.gcMarkWorkerStartTime = startTime  
  77.         decnwait := atomic.Xadd(&work.nwait, -1)  
  78.         if decnwait == work.nproc {  
  79.             println("runtime: workwork.nwait=", decnwait, "work.nproc=", work.nproc)  
  80.             throw("work.nwait was > work.nproc")  
  81.         }  
  82.         // 切換到g0工作  
  83.         systemstack(func() {  
  84.             // Mark our goroutine preemptible so its stack  
  85.             // can be scanned. This lets two mark workers  
  86.             // scan each other (otherwise, they would  
  87.             // deadlock). We must not modify anything on  
  88.             // the G stack. However, stack shrinking is  
  89.             // disabled for mark workers, so it is safe to  
  90.             // read from the G stack.  
  91.             // 設(shè)置G的狀態(tài)為waiting,以便于另一個(gè)g掃描它的棧(兩個(gè)g可以互相掃描對(duì)方的棧)  
  92.             casgstatus(gp, _Grunning, _Gwaiting)  
  93.             switch _p_.gcMarkWorkerMode {  
  94.             default:  
  95.                 throw("gcBgMarkWorker: unexpected gcMarkWorkerMode")  
  96.             case gcMarkWorkerDedicatedMode:  
  97.                 // 專心執(zhí)行標(biāo)記工作的模式  
  98.                 gcDrain(&_p_.gcw, gcDrainUntilPreempt|gcDrainFlushBgCredit)  
  99.                 if gp.preempt {  
  100.                     // 被搶占了,把所有本地運(yùn)行隊(duì)列中的G放到全局運(yùn)行隊(duì)列中  
  101.                     // We were preempted. This is  
  102.                     // a useful signal to kick  
  103.                     // everything out of the run  
  104.                     // queue so it can run  
  105.                     // somewhere else.  
  106.                     lock(&sched.lock)  
  107.                     for {  
  108.                         gp, _ := runqget(_p_)  
  109.                         if gp == nil {  
  110.                             break  
  111.                         }  
  112.                         globrunqput(gp)  
  113.                     }  
  114.                     unlock(&sched.lock)  
  115.                 }  
  116.                 // Go back to draining, this time  
  117.                 // without preemption.  
  118.                 // 繼續(xù)執(zhí)行標(biāo)記工作  
  119.                 gcDrain(&_p_.gcw, gcDrainNoBlock|gcDrainFlushBgCredit)  
  120.             case gcMarkWorkerFractionalMode:  
  121.                 // 執(zhí)行標(biāo)記工作,知道被搶占  
  122.                 gcDrain(&_p_.gcw, gcDrainFractional|gcDrainUntilPreempt|gcDrainFlushBgCredit)  
  123.             case gcMarkWorkerIdleMode:  
  124.                 // 空閑的時(shí)候執(zhí)行標(biāo)記工作  
  125.                 gcDrain(&_p_.gcw, gcDrainIdle|gcDrainUntilPreempt|gcDrainFlushBgCredit)  
  126.             }  
  127.             // 把G的waiting狀態(tài)轉(zhuǎn)換到runing狀態(tài)  
  128.             casgstatus(gp, _Gwaiting, _Grunning)  
  129.         })  
  130.         // If we are nearing the end of mark, dispose  
  131.         // of the cache promptly. We must do this  
  132.         // before signaling that we're no longer  
  133.         // working so that other workers can't observe  
  134.         // no workers and no work while we have this  
  135.         // cached, and before we compute done.  
  136.         // 及時(shí)處理本地緩存,上交到全局的隊(duì)列中  
  137.         if gcBlackenPromptly {  
  138.             _p_.gcw.dispose()  
  139.         }  
  140.         // Account for time.  
  141.         // 累加耗時(shí)  
  142.         duration := nanotime() - startTime  
  143.         switch _p_.gcMarkWorkerMode {  
  144.         case gcMarkWorkerDedicatedMode:  
  145.             atomic.Xaddint64(&gcController.dedicatedMarkTime, duration)  
  146.             atomic.Xaddint64(&gcController.dedicatedMarkWorkersNeeded, 1)  
  147.         case gcMarkWorkerFractionalMode:  
  148.             atomic.Xaddint64(&gcController.fractionalMarkTime, duration)  
  149.             atomic.Xaddint64(&_p_.gcFractionalMarkTime, duration)  
  150.         case gcMarkWorkerIdleMode:  
  151.             atomic.Xaddint64(&gcController.idleMarkTime, duration)  
  152.         }  
  153.         // Was this the last worker and did we run out  
  154.         // of work?  
  155.         incnwait := atomic.Xadd(&work.nwait, +1)  
  156.         if incnwait > work.nproc {  
  157.             println("runtime: p.gcMarkWorkerMode=", _p_.gcMarkWorkerMode,  
  158.                 "workwork.nwait=", incnwait, "work.nproc=", work.nproc)  
  159.             throw("work.nwait > work.nproc")  
  160.         }  
  161.         // If this worker reached a background mark completion  
  162.         // point, signal the main GC goroutine.  
  163.         if incnwait == work.nproc && !gcMarkWorkAvailable(nil) {  
  164.             // Make this G preemptible and disassociate it  
  165.             // as the worker for this P so  
  166.             // findRunnableGCWorker doesn't try to  
  167.             // schedule it.  
  168.             // 取消p m的關(guān)聯(lián)  
  169.             _p_.gcBgMarkWorker.set(nil)  
  170.             releasem(park.m.ptr())  
  171.             gcMarkDone()  
  172.             // Disable preemption and prepare to reattach  
  173.             // to the P.  
  174.             //  
  175.             // We may be running on a different P at this  
  176.             // point, so we can't reattach until this G is  
  177.             // parked.  
  178.             park.m.set(acquirem())  
  179.             park.attach.set(_p_)  
  180.         }  

  181. 標(biāo)題名稱:深入理解Go-垃圾回收機(jī)制
    分享地址:http://www.dlmjj.cn/article/dhcepdc.html