Golang源码探索----GC的实现原理(5)

推荐文章:

Golang源码探索----GC的实现原理(1)

Golang源码探索----GC的实现原理(2)

Golang源码探索----GC的实现原理(3)

Golang源码探索----GC的实现原理(4)

gcDrain函数扫描完根对象, 就会开始消费标记队列, 对从标记队列中取出的对象调用scanobject函数:

  1// scanobject scans the object starting at b, adding pointers to gcw.
  2// b must point to the beginning of a heap object or an oblet.
  3// scanobject consults the GC bitmap for the pointer mask and the
  4// spans for the size of the object.
  5//
  6//go:nowritebarrier
  7func scanobject(b uintptr, gcw *gcWork) {
  8    // Note that arena_used may change concurrently during
  9    // scanobject and hence scanobject may encounter a pointer to
 10    // a newly allocated heap object that is *not* in
 11    // [start,used). It will not mark this object; however, we
 12    // know that it was just installed by a mutator, which means
 13    // that mutator will execute a write barrier and take care of
 14    // marking it. This is even more pronounced on relaxed memory
 15    // architectures since we access arena_used without barriers
 16    // or synchronization, but the same logic applies.
 17    arena_start := mheap_.arena_start
 18    arena_used := mheap_.arena_used
 19    // Find the bits for b and the size of the object at b.
 20    //
 21    // b is either the beginning of an object, in which case this
 22    // is the size of the object to scan, or it points to an
 23    // oblet, in which case we compute the size to scan below.
 24    // 获取对象对应的bitmap
 25    hbits := heapBitsForAddr(b)
 26    // 获取对象所在的span
 27    s := spanOfUnchecked(b)
 28    // 获取对象的大小
 29    n := s.elemsize
 30    if n == 0 {
 31        throw("scanobject n == 0")
 32    }
 33    // 对象大小过大时(maxObletBytes是128KB)需要分割扫描
 34    // 每次最多只扫描128KB
 35    if n > maxObletBytes {
 36        // Large object. Break into oblets for better
 37        // parallelism and lower latency.
 38        if b == s.base() {
 39            // It's possible this is a noscan object (not
 40            // from greyobject, but from other code
 41            // paths), in which case we must *not* enqueue
 42            // oblets since their bitmaps will be
 43            // uninitialized.
 44            if s.spanclass.noscan() {
 45                // Bypass the whole scan.
 46                gcw.bytesMarked += uint64(n)
 47                return
 48            }
 49            // Enqueue the other oblets to scan later.
 50            // Some oblets may be in b's scalar tail, but
 51            // these will be marked as "no more pointers",
 52            // so we'll drop out immediately when we go to
 53            // scan those.
 54            for oblet := b + maxObletBytes; oblet < s.base()+s.elemsize; oblet += maxObletBytes {
 55                if !gcw.putFast(oblet) {
 56                    gcw.put(oblet)
 57                }
 58            }
 59        }
 60        // Compute the size of the oblet. Since this object
 61        // must be a large object, s.base() is the beginning
 62        // of the object.
 63        n = s.base() + s.elemsize - b
 64        if n > maxObletBytes {
 65            n = maxObletBytes
 66        }
 67    }
 68    // 扫描对象中的指针
 69    var i uintptr
 70    for i = 0; i < n; i += sys.PtrSize {
 71        // 获取对应的bit
 72        // Find bits for this word.
 73        if i != 0 {
 74            // Avoid needless hbits.next() on last iteration.
 75            hbits = hbits.next()
 76        }
 77        // Load bits once. See CL 22712 and issue 16973 for discussion.
 78        bits := hbits.bits()
 79        // 检查scan bit判断是否继续扫描, 注意第二个scan bit是checkmark
 80        // During checkmarking, 1-word objects store the checkmark
 81        // in the type bit for the one word. The only one-word objects
 82        // are pointers, or else they'd be merged with other non-pointer
 83        // data into larger allocations.
 84        if i != 1*sys.PtrSize && bits&bitScan == 0 {
 85            break // no more pointers in this object
 86        }
 87        // 检查pointer bit, 不是指针则继续
 88        if bits&bitPointer == 0 {
 89            continue // not a pointer
 90        }
 91        // 取出指针的值
 92        // Work here is duplicated in scanblock and above.
 93        // If you make changes here, make changes there too.
 94        obj := *(*uintptr)(unsafe.Pointer(b + i))
 95        // 如果指针在arena区域中, 则调用greyobject标记对象并把对象放到标记队列中
 96        // At this point we have extracted the next potential pointer.
 97        // Check if it points into heap and not back at the current object.
 98        if obj != 0 && arena_start <= obj && obj < arena_used && obj-b >= n {
 99            // Mark the object.
100            if obj, hbits, span, objIndex := heapBitsForObject(obj, b, i); obj != 0 {
101                greyobject(obj, b, i, hbits, span, gcw, objIndex)
102            }
103        }
104    }
105    // 统计扫描过的大小和对象数量
106    gcw.bytesMarked += uint64(n)
107    gcw.scanWork += int64(i)
108}

在所有后台标记任务都把标记队列消费完毕时, 会执行gcMarkDone函数准备进入完成标记阶段(mark termination):
在并行GC中gcMarkDone会被执行两次, 第一次会禁止本地标记队列然后重新开始后台标记任务, 第二次会进入完成标记阶段(mark termination)。

  1// gcMarkDone transitions the GC from mark 1 to mark 2 and from mark 2
  2// to mark termination.
  3//
  4// This should be called when all mark work has been drained. In mark
  5// 1, this includes all root marking jobs, global work buffers, and
  6// active work buffers in assists and background workers; however,
  7// work may still be cached in per-P work buffers. In mark 2, per-P
  8// caches are disabled.
  9//
 10// The calling context must be preemptible.
 11//
 12// Note that it is explicitly okay to have write barriers in this
 13// function because completion of concurrent mark is best-effort
 14// anyway. Any work created by write barriers here will be cleaned up
 15// by mark termination.
 16func gcMarkDone() {
 17top:
 18    semacquire(&work.markDoneSema)
 19    // Re-check transition condition under transition lock.
 20    if !(gcphase == _GCmark && work.nwait == work.nproc && !gcMarkWorkAvailable(nil)) {
 21        semrelease(&work.markDoneSema)
 22        return
 23    }
 24    // 暂时禁止启动新的后台标记任务
 25    // Disallow starting new workers so that any remaining workers
 26    // in the current mark phase will drain out.
 27    //
 28    // TODO(austin): Should dedicated workers keep an eye on this
 29    // and exit gcDrain promptly?
 30    atomic.Xaddint64(&gcController.dedicatedMarkWorkersNeeded, -0xffffffff)
 31    atomic.Xaddint64(&gcController.fractionalMarkWorkersNeeded, -0xffffffff)
 32    // 判断本地标记队列是否已禁用
 33    if !gcBlackenPromptly {
 34        // 本地标记队列是否未禁用, 禁用然后重新开始后台标记任务
 35        // Transition from mark 1 to mark 2.
 36        //
 37        // The global work list is empty, but there can still be work
 38        // sitting in the per-P work caches.
 39        // Flush and disable work caches.
 40        // 禁用本地标记队列
 41        // Disallow caching workbufs and indicate that we're in mark 2.
 42        gcBlackenPromptly = true
 43        // Prevent completion of mark 2 until we've flushed
 44        // cached workbufs.
 45        atomic.Xadd(&work.nwait, -1)
 46        // GC is set up for mark 2. Let Gs blocked on the
 47        // transition lock go while we flush caches.
 48        semrelease(&work.markDoneSema)
 49        // 把所有本地标记队列中的对象都推到全局标记队列
 50        systemstack(func() {
 51            // Flush all currently cached workbufs and
 52            // ensure all Ps see gcBlackenPromptly. This
 53            // also blocks until any remaining mark 1
 54            // workers have exited their loop so we can
 55            // start new mark 2 workers.
 56            forEachP(func(_p_ *p) {
 57                _p_.gcw.dispose()
 58            })
 59        })
 60        // 除错用
 61        // Check that roots are marked. We should be able to
 62        // do this before the forEachP, but based on issue
 63        // #16083 there may be a (harmless) race where we can
 64        // enter mark 2 while some workers are still scanning
 65        // stacks. The forEachP ensures these scans are done.
 66        //
 67        // TODO(austin): Figure out the race and fix this
 68        // properly.
 69        gcMarkRootCheck()
 70        // 允许启动新的后台标记任务
 71        // Now we can start up mark 2 workers.
 72        atomic.Xaddint64(&gcController.dedicatedMarkWorkersNeeded, 0xffffffff)
 73        atomic.Xaddint64(&gcController.fractionalMarkWorkersNeeded, 0xffffffff)
 74        // 如果确定没有更多的任务则可以直接跳到函数顶部
 75        // 这样就当作是第二次调用了
 76        incnwait := atomic.Xadd(&work.nwait, +1)
 77        if incnwait == work.nproc && !gcMarkWorkAvailable(nil) {
 78            // This loop will make progress because
 79            // gcBlackenPromptly is now true, so it won't
 80            // take this same "if" branch.
 81            goto top
 82        }
 83    } else {
 84        // 记录完成标记阶段开始的时间和STW开始的时间
 85        // Transition to mark termination.
 86        now := nanotime()
 87        work.tMarkTerm = now
 88        work.pauseStart = now
 89        // 禁止G被抢占
 90        getg().m.preemptoff = "gcing"
 91        // 停止所有运行中的G, 并禁止它们运行
 92        systemstack(stopTheWorldWithSema)
 93        // !!!!!!!!!!!!!!!!
 94        // 世界已停止(STW)...
 95        // !!!!!!!!!!!!!!!!
 96        // The gcphase is _GCmark, it will transition to _GCmarktermination
 97        // below. The important thing is that the wb remains active until
 98        // all marking is complete. This includes writes made by the GC.
 99        // 标记对根对象的扫描已完成, 会影响gcMarkRootPrepare中的处理
100        // Record that one root marking pass has completed.
101        work.markrootDone = true
102        // 禁止辅助GC和后台标记任务的运行
103        // Disable assists and background workers. We must do
104        // this before waking blocked assists.
105        atomic.Store(&gcBlackenEnabled, 0)
106        // 唤醒所有因为辅助GC而休眠的G
107        // Wake all blocked assists. These will run when we
108        // start the world again.
109        gcWakeAllAssists()
110        // Likewise, release the transition lock. Blocked
111        // workers and assists will run when we start the
112        // world again.
113        semrelease(&work.markDoneSema)
114        // 计算下一次触发gc需要的heap大小
115        // endCycle depends on all gcWork cache stats being
116        // flushed. This is ensured by mark 2.
117        nextTriggerRatio := gcController.endCycle()
118        // 进入完成标记阶段, 会重新启动世界
119        // Perform mark termination. This will restart the world.
120        gcMarkTermination(nextTriggerRatio)
121    }
122}

gcMarkTermination函数会进入完成标记阶段:

  1func gcMarkTermination(nextTriggerRatio float64) {
  2    // World is stopped.
  3    // Start marktermination which includes enabling the write barrier.
  4    // 禁止辅助GC和后台标记任务的运行
  5    atomic.Store(&gcBlackenEnabled, 0)
  6    // 重新允许本地标记队列(下次GC使用)
  7    gcBlackenPromptly = false
  8    // 设置当前GC阶段到完成标记阶段, 并启用写屏障
  9    setGCPhase(_GCmarktermination)
 10    // 记录开始时间
 11    work.heap1 = memstats.heap_live
 12    startTime := nanotime()
 13    // 禁止G被抢占
 14    mp := acquirem()
 15    mp.preemptoff = "gcing"
 16    _g_ := getg()
 17    _g_.m.traceback = 2
 18    // 设置G的状态为等待中这样它的栈可以被扫描
 19    gp := _g_.m.curg
 20    casgstatus(gp, _Grunning, _Gwaiting)
 21    gp.waitreason = "garbage collection"
 22    // 切换到g0运行
 23    // Run gc on the g0 stack. We do this so that the g stack
 24    // we're currently running on will no longer change. Cuts
 25    // the root set down a bit (g0 stacks are not scanned, and
 26    // we don't need to scan gc's internal state).  We also
 27    // need to switch to g0 so we can shrink the stack.
 28    systemstack(func() {
 29        // 开始STW中的标记
 30        gcMark(startTime)
 31        // 必须立刻返回, 因为外面的G的栈有可能被移动, 不能在这之后访问外面的变量
 32        // Must return immediately.
 33        // The outer function's stack may have moved
 34        // during gcMark (it shrinks stacks, including the
 35        // outer function's stack), so we must not refer
 36        // to any of its variables. Return back to the
 37        // non-system stack to pick up the new addresses
 38        // before continuing.
 39    })
 40    // 重新切换到g0运行
 41    systemstack(func() {
 42        work.heap2 = work.bytesMarked
 43        // 如果启用了checkmark则执行检查, 检查是否所有可到达的对象都有标记
 44        if debug.gccheckmark > 0 {
 45            // Run a full stop-the-world mark using checkmark bits,
 46            // to check that we didn't forget to mark anything during
 47            // the concurrent mark process.
 48            gcResetMarkState()
 49            initCheckmarks()
 50            gcMark(startTime)
 51            clearCheckmarks()
 52        }
 53        // 设置当前GC阶段到关闭, 并禁用写屏障
 54        // marking is complete so we can turn the write barrier off
 55        setGCPhase(_GCoff)
 56        // 唤醒后台清扫任务, 将在STW结束后开始运行
 57        gcSweep(work.mode)
 58        // 除错用
 59        if debug.gctrace > 1 {
 60            startTime = nanotime()
 61            // The g stacks have been scanned so
 62            // they have gcscanvalid==true and gcworkdone==true.
 63            // Reset these so that all stacks will be rescanned.
 64            gcResetMarkState()
 65            finishsweep_m()
 66            // Still in STW but gcphase is _GCoff, reset to _GCmarktermination
 67            // At this point all objects will be found during the gcMark which
 68            // does a complete STW mark and object scan.
 69            setGCPhase(_GCmarktermination)
 70            gcMark(startTime)
 71            setGCPhase(_GCoff) // marking is done, turn off wb.
 72            gcSweep(work.mode)
 73        }
 74    })
 75    // 设置G的状态为运行中
 76    _g_.m.traceback = 0
 77    casgstatus(gp, _Gwaiting, _Grunning)
 78    // 跟踪处理
 79    if trace.enabled {
 80        traceGCDone()
 81    }
 82    // all done
 83    mp.preemptoff = ""
 84    if gcphase != _GCoff {
 85        throw("gc done but gcphase != _GCoff")
 86    }
 87    // 更新下一次触发gc需要的heap大小(gc_trigger)
 88    // Update GC trigger and pacing for the next cycle.
 89    gcSetTriggerRatio(nextTriggerRatio)
 90    // 更新用时记录
 91    // Update timing memstats
 92    now := nanotime()
 93    sec, nsec, _ := time_now()
 94    unixNow := sec*1e9 + int64(nsec)
 95    work.pauseNS += now - work.pauseStart
 96    work.tEnd = now
 97    atomic.Store64(&memstats.last_gc_unix, uint64(unixNow)) // must be Unix time to make sense to user
 98    atomic.Store64(&memstats.last_gc_nanotime, uint64(now)) // monotonic time for us
 99    memstats.pause_ns[memstats.numgc%uint32(len(memstats.pause_ns))] = uint64(work.pauseNS)
100    memstats.pause_end[memstats.numgc%uint32(len(memstats.pause_end))] = uint64(unixNow)
101    memstats.pause_total_ns += uint64(work.pauseNS)
102    // 更新所用cpu记录
103    // Update work.totaltime.
104    sweepTermCpu := int64(work.stwprocs) * (work.tMark - work.tSweepTerm)
105    // We report idle marking time below, but omit it from the
106    // overall utilization here since it's "free".
107    markCpu := gcController.assistTime + gcController.dedicatedMarkTime + gcController.fractionalMarkTime
108    markTermCpu := int64(work.stwprocs) * (work.tEnd - work.tMarkTerm)
109    cycleCpu := sweepTermCpu + markCpu + markTermCpu
110    work.totaltime += cycleCpu
111    // Compute overall GC CPU utilization.
112    totalCpu := sched.totaltime + (now-sched.procresizetime)*int64(gomaxprocs)
113    memstats.gc_cpu_fraction = float64(work.totaltime) / float64(totalCpu)
114    // 重置清扫状态
115    // Reset sweep state.
116    sweep.nbgsweep = 0
117    sweep.npausesweep = 0
118    // 统计强制开始GC的次数
119    if work.userForced {
120        memstats.numforcedgc++
121    }
122    // 统计执行GC的次数然后唤醒等待清扫的G
123    // Bump GC cycle count and wake goroutines waiting on sweep.
124    lock(&work.sweepWaiters.lock)
125    memstats.numgc++
126    injectglist(work.sweepWaiters.head.ptr())
127    work.sweepWaiters.head = 0
128    unlock(&work.sweepWaiters.lock)
129    // 性能统计用
130    // Finish the current heap profiling cycle and start a new
131    // heap profiling cycle. We do this before starting the world
132    // so events don't leak into the wrong cycle.
133    mProf_NextCycle()
134    // 重新启动世界
135    systemstack(startTheWorldWithSema)
136    // !!!!!!!!!!!!!!!
137    // 世界已重新启动...
138    // !!!!!!!!!!!!!!!
139    // 性能统计用
140    // Flush the heap profile so we can start a new cycle next GC.
141    // This is relatively expensive, so we don't do it with the
142    // world stopped.
143    mProf_Flush()
144    // 移动标记队列使用的缓冲区到自由列表, 使得它们可以被回收
145    // Prepare workbufs for freeing by the sweeper. We do this
146    // asynchronously because it can take non-trivial time.
147    prepareFreeWorkbufs()
148    // 释放未使用的栈
149    // Free stack spans. This must be done between GC cycles.
150    systemstack(freeStackSpans)
151    // 除错用
152    // Print gctrace before dropping worldsema. As soon as we drop
153    // worldsema another cycle could start and smash the stats
154    // we're trying to print.
155    if debug.gctrace > 0 {
156        util := int(memstats.gc_cpu_fraction * 100)
157        var sbuf [24]byte
158        printlock()
159        print("gc ", memstats.numgc,
160            " @", string(itoaDiv(sbuf[:], uint64(work.tSweepTerm-runtimeInitTime)/1e6, 3)), "s ",
161            util, "%: ")
162        prev := work.tSweepTerm
163        for i, ns := range []int64{work.tMark, work.tMarkTerm, work.tEnd} {
164            if i != 0 {
165                print("+")
166            }
167            print(string(fmtNSAsMS(sbuf[:], uint64(ns-prev))))
168            prev = ns
169        }
170        print(" ms clock, ")
171        for i, ns := range []int64{sweepTermCpu, gcController.assistTime, gcController.dedicatedMarkTime + gcController.fractionalMarkTime, gcController.idleMarkTime, markTermCpu} {
172            if i == 2 || i == 3 {
173                // Separate mark time components with /.
174                print("/")
175            } else if i != 0 {
176                print("+")
177            }
178            print(string(fmtNSAsMS(sbuf[:], uint64(ns))))
179        }
180        print(" ms cpu, ",
181            work.heap0>>20, "->", work.heap1>>20, "->", work.heap2>>20, " MB, ",
182            work.heapGoal>>20, " MB goal, ",
183            work.maxprocs, " P")
184        if work.userForced {
185            print(" (forced)")
186        }
187        print("\n")
188        printunlock()
189    }
190    semrelease(&worldsema)
191    // Careful: another GC cycle may start now.
192    // 重新允许当前的G被抢占
193    releasem(mp)
194    mp = nil
195    // 如果是并行GC, 让当前M继续运行(会回到gcBgMarkWorker然后休眠)
196    // 如果不是并行GC, 则让当前M开始调度
197    // now that gc is done, kick off finalizer thread if needed
198    if !concurrentSweep {
199        // give the queued finalizers, if any, a chance to run
200        Gosched()
201    }
202}

gcSweep函数会唤醒后台清扫任务:
后台清扫任务会在程序启动时调用的gcenable函数中启动.

 1func gcSweep(mode gcMode) {
 2    if gcphase != _GCoff {
 3        throw("gcSweep being done but phase is not GCoff")
 4    }
 5    // 增加sweepgen, 这样sweepSpans中两个队列角色会交换, 所有span都会变为"待清扫"的span
 6    lock(&mheap_.lock)
 7    mheap_.sweepgen += 2
 8    mheap_.sweepdone = 0
 9    if mheap_.sweepSpans[mheap_.sweepgen/2%2].index != 0 {
10        // We should have drained this list during the last
11        // sweep phase. We certainly need to start this phase
12        // with an empty swept list.
13        throw("non-empty swept list")
14    }
15    mheap_.pagesSwept = 0
16    unlock(&mheap_.lock)
17    // 如果非并行GC则在这里完成所有工作(STW中)
18    if !_ConcurrentSweep || mode == gcForceBlockMode {
19        // Special case synchronous sweep.
20        // Record that no proportional sweeping has to happen.
21        lock(&mheap_.lock)
22        mheap_.sweepPagesPerByte = 0
23        unlock(&mheap_.lock)
24        // Sweep all spans eagerly.
25        for sweepone() != ^uintptr(0) {
26            sweep.npausesweep++
27        }
28        // Free workbufs eagerly.
29        prepareFreeWorkbufs()
30        for freeSomeWbufs(false) {
31        }
32        // All "free" events for this mark/sweep cycle have
33        // now happened, so we can make this profile cycle
34        // available immediately.
35        mProf_NextCycle()
36        mProf_Flush()
37        return
38    }
39    // 唤醒后台清扫任务
40    // Background sweep.
41    lock(&sweep.lock)
42    if sweep.parked {
43        sweep.parked = false
44        ready(sweep.g, 0, true)
45    }
46    unlock(&sweep.lock)
47}
48

后台清扫任务的函数是bgsweep:

 1func bgsweep(c chan int) {
 2    sweep.g = getg()
 3    // 等待唤醒
 4    lock(&sweep.lock)
 5    sweep.parked = true
 6    c <- 1
 7    goparkunlock(&sweep.lock, "GC sweep wait", traceEvGoBlock, 1)
 8    // 循环清扫
 9    for {
10        // 清扫一个span, 然后进入调度(一次只做少量工作)
11        for gosweepone() != ^uintptr(0) {
12            sweep.nbgsweep++
13            Gosched()
14        }
15        // 释放一些未使用的标记队列缓冲区到heap
16        for freeSomeWbufs(true) {
17            Gosched()
18        }
19        // 如果清扫未完成则继续循环
20        lock(&sweep.lock)
21        if !gosweepdone() {
22            // This can happen if a GC runs between
23            // gosweepone returning ^0 above
24            // and the lock being acquired.
25            unlock(&sweep.lock)
26            continue
27        }
28        // 否则让后台清扫任务进入休眠, 当前M继续调度
29        sweep.parked = true
30        goparkunlock(&sweep.lock, "GC sweep wait", traceEvGoBlock, 1)
31    }
32}
33

gosweepone函数会从sweepSpans中取出单个span清扫:

1//go:nowritebarrier
2func gosweepone() uintptr {
3    var ret uintptr
4    // 切换到g0运行
5    systemstack(func() {
6        ret = sweepone()
7    })
8    return ret
9}

sweepone函数如下:

 1// sweeps one span
 2// returns number of pages returned to heap, or ^uintptr(0) if there is nothing to sweep
 3//go:nowritebarrier
 4func sweepone() uintptr {
 5    _g_ := getg()
 6    sweepRatio := mheap_.sweepPagesPerByte // For debugging
 7    // 禁止G被抢占
 8    // increment locks to ensure that the goroutine is not preempted
 9    // in the middle of sweep thus leaving the span in an inconsistent state for next GC
10    _g_.m.locks++
11    // 检查是否已完成清扫
12    if atomic.Load(&mheap_.sweepdone) != 0 {
13        _g_.m.locks--
14        return ^uintptr(0)
15    }
16    // 更新同时执行sweep的任务数量
17    atomic.Xadd(&mheap_.sweepers, +1)
18    npages := ^uintptr(0)
19    sg := mheap_.sweepgen
20    for {
21        // 从sweepSpans中取出一个span
22        s := mheap_.sweepSpans[1-sg/2%2].pop()
23        // 全部清扫完毕时跳出循环
24        if s == nil {
25            atomic.Store(&mheap_.sweepdone, 1)
26            break
27        }
28        // 其他M已经在清扫这个span时跳过
29        if s.state != mSpanInUse {
30            // This can happen if direct sweeping already
31            // swept this span, but in that case the sweep
32            // generation should always be up-to-date.
33            if s.sweepgen != sg {
34                print("runtime: bad span s.state=", s.state, " s.sweepgen=", s.sweepgen, " sweepgen=", sg, "\n")
35                throw("non in-use span in unswept list")
36            }
37            continue
38        }
39        // 原子增加span的sweepgen, 失败表示其他M已经开始清扫这个span, 跳过
40        if s.sweepgen != sg-2 || !atomic.Cas(&s.sweepgen, sg-2, sg-1) {
41            continue
42        }
43        // 清扫这个span, 然后跳出循环
44        npages = s.npages
45        if !s.sweep(false) {
46            // Span is still in-use, so this returned no
47            // pages to the heap and the span needs to
48            // move to the swept in-use list.
49            npages = 0
50        }
51        break
52    }
53    // 更新同时执行sweep的任务数量
54    // Decrement the number of active sweepers and if this is the
55    // last one print trace information.
56    if atomic.Xadd(&mheap_.sweepers, -1) == 0 && atomic.Load(&mheap_.sweepdone) != 0 {
57        if debug.gcpacertrace > 0 {
58            print("pacer: sweep done at heap size ", memstats.heap_live>>20, "MB; allocated ", (memstats.heap_live-mheap_.sweepHeapLiveBasis)>>20, "MB during sweep; swept ", mheap_.pagesSwept, " pages at ", sweepRatio, " pages/byte\n")
59        }
60    }
61    // 允许G被抢占
62    _g_.m.locks--
63    // 返回清扫的页数
64    return npages
65}

版权申明:内容来源网络,版权归原创者所有。除非无法确认,我们都会标明作者及出处,如有侵权烦请告知,我们会立即删除并表示歉意。谢谢。


Golang语言社区

ID:GolangWeb

www.ByteEdu.Com

游戏服务器架构丨分布式技术丨大数据丨游戏算法学习

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值