作者:@daemon365
本文为作者原创,转载请注明出处:https://www.cnblogs.com/daemon365/p/18618851
*** Go 代码基于 v1.23.0 ***
介绍
触发条件
- 用户调用
runtime.GC
主动触发
- Go 程序检测到距上次 GC 内存分配增长超过一定比例时(默认 100%)触发
- 定时触发 (默认 2 min)
定时触发
代码在 src/runtime/proc.go
中
| func init() { |
| go forcegchelper() |
| } |
| |
| |
| func forcegchelper() { |
| forcegc.g = getg() |
| lockInit(&forcegc.lock, lockRankForcegc) |
| for { |
| lock(&forcegc.lock) |
| if forcegc.idle.Load() { |
| throw("forcegc: phase error") |
| } |
| forcegc.idle.Store(true) |
| |
| goparkunlock(&forcegc.lock, waitReasonForceGCIdle, traceBlockSystemGoroutine, 1) |
| |
| if debug.gctrace > 0 { |
| println("GC forced") |
| } |
| |
| gcStart(gcTrigger{kind: gcTriggerTime, now: nanotime()}) |
| } |
| } |
那么唤醒 goroutine 的逻辑代码在哪里呢?在 sysmon
物理线程中
| func sysmon() { |
| |
| for { |
| |
| |
| |
| if t := (gcTrigger{kind: gcTriggerTime, now: now}); t.test() && forcegc.idle.Load() { |
| lock(&forcegc.lock) |
| forcegc.idle.Store(false) |
| |
| var list gList |
| list.push(forcegc.g) |
| injectglist(&list) |
| unlock(&forcegc.lock) |
| } |
| |
| } |
| } |
| |
| func injectglist(glist *gList) { |
| |
| |
| |
| startIdle := func(n int) { |
| for i := 0; i < n; i++ { |
| mp := acquirem() |
| lock(&sched.lock) |
| |
| pp, _ := pidlegetSpinning(0) |
| if pp == nil { |
| unlock(&sched.lock) |
| releasem(mp) |
| break |
| } |
| |
| startm(pp, false, true) |
| unlock(&sched.lock) |
| releasem(mp) |
| } |
| } |
| |
| pp := getg().m.p.ptr() |
| if pp == nil { |
| |
| lock(&sched.lock) |
| globrunqputbatch(&q, int32(qsize)) |
| unlock(&sched.lock) |
| startIdle(qsize) |
| return |
| } |
| |
| npidle := int(sched.npidle.Load()) |
| var ( |
| globq gQueue |
| n int |
| ) |
| for n = 0; n < npidle && !q.empty(); n++ { |
| g := q.pop() |
| globq.pushBack(g) |
| } |
| if n > 0 { |
| lock(&sched.lock) |
| globrunqputbatch(&globq, int32(n)) |
| unlock(&sched.lock) |
| startIdle(n) |
| qsize -= n |
| } |
| |
| |
| if !q.empty() { |
| runqputbatch(pp, &q, qsize) |
| } |
| |
| |
| wakep() |
| } |
test
- 根据 gcTrigger 的类型检测是否应该触发垃圾收集。
| type gcTrigger struct { |
| kind gcTriggerKind |
| now int64 |
| n uint32 |
| } |
| |
| type gcTriggerKind int |
| |
| const ( |
| gcTriggerHeap gcTriggerKind = iota |
| gcTriggerTime |
| gcTriggerCycle |
| ) |
| |
| |
| func (t gcTrigger) test() bool { |
| |
| if !memstats.enablegc || panicking.Load() != 0 || gcphase != _GCoff { |
| return false |
| } |
| switch t.kind { |
| case gcTriggerHeap: |
| |
| trigger, _ := gcController.trigger() |
| return gcController.heapLive.Load() >= trigger |
| case gcTriggerTime: |
| |
| if gcController.gcPercent.Load() < 0 { |
| return false |
| } |
| |
| lastgc := int64(atomic.Load64(&memstats.last_gc_nanotime)) |
| return lastgc != 0 && t.now-lastgc > forcegcperiod |
| case gcTriggerCycle: |
| |
| return int32(t.n-work.cycles.Load()) > 0 |
| } |
| return true |
| } |
| |
Heap 增长触发
mallocgc
不但 malloc 了内存,还会检查是否需要触发 gc
| func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer { |
| assistG := deductAssistCredit(size) |
| |
| shouldhelpgc := false |
| |
| |
| if size <= maxSmallSize-mallocHeaderSize { |
| |
| |
| v, span, shouldhelpgc = c.nextFree(tinySpanClass) |
| |
| } else { |
| |
| shouldhelpgc = true |
| |
| } |
| |
| |
| if gcphase != _GCoff { |
| gcmarknewobject(span, uintptr(x)) |
| } |
| |
| |
| if shouldhelpgc { |
| if t := (gcTrigger{kind: gcTriggerHeap}); t.test() { |
| gcStart(t) |
| } |
| } |
| } |
| func gcmarknewobject(span *mspan, obj uintptr) { |
| span.markBitsForIndex(objIndex).setMarked() |
| } |
| |
主动触发
| func GC() { |
| |
| gcStart(gcTrigger{kind: gcTriggerCycle, n: n + 1}) |
| |
| } |
gcStart
gcStart 为 gc 的标记主流程
| func gcStart(trigger gcTrigger) { |
| |
| |
| |
| for trigger.test() && sweepone() != ^uintptr(0) { |
| } |
| |
| |
| semacquire(&work.startSema) |
| |
| if !trigger.test() { |
| semrelease(&work.startSema) |
| return |
| } |
| |
| |
| |
| |
| gcBgMarkStartWorkers() |
| |
| |
| systemstack(gcResetMarkState) |
| |
| |
| systemstack(func() { |
| stw = stopTheWorldWithSema(stwGCSweepTerm) |
| }) |
| |
| |
| |
| systemstack(func() { |
| finishsweep_m() |
| }) |
| |
| |
| |
| |
| gcController.startCycle(now, int(gomaxprocs), trigger) |
| |
| |
| |
| |
| setGCPhase(_GCmark) |
| |
| |
| gcMarkTinyAllocs() |
| |
| |
| systemstack(func() { |
| |
| now = startTheWorldWithSema(0, stw) |
| work.pauseNS += now - stw.startedStopping |
| work.tMark = now |
| |
| |
| gcCPULimiter.finishGCTransition(now) |
| }) |
| |
| |
| |
| semrelease(&worldsema) |
| releasem(mp) |
| |
| |
| if mode != gcBackgroundMode { |
| Gosched() |
| } |
| |
| semrelease(&work.startSema) |
| } |
gcBgMarkStartWorkers
gcBgMarkStartWorkers 启动 P 个数个 goroutine 去做并发标记
| func gcBgMarkStartWorkers() { |
| |
| |
| for gcBgMarkWorkerCount < gomaxprocs { |
| go gcBgMarkWorker(ready) |
| releasem(mp) |
| <-ready |
| } |
| |
| if incnwait == work.nproc && !gcMarkWorkAvailable(nil) { |
| releasem(node.m.ptr()) |
| node.m.set(nil) |
| |
| gcMarkDone() |
| } |
| } |
| func gcBgMarkWorker(ready chan struct{}) { |
| gp := getg() |
| |
| node := new(gcBgMarkWorkerNode) |
| node.gp.set(gp) |
| node.m.set(acquirem()) |
| |
| ready <- struct{}{} |
| for { |
| |
| gopark(func(g *g, nodep unsafe.Pointer) bool { |
| node := (*gcBgMarkWorkerNode)(nodep) |
| if mp := node.m.ptr(); mp != nil { |
| releasem(mp) |
| } |
| gcBgMarkWorkerPool.push(&node.node) |
| return true |
| }, unsafe.Pointer(node), waitReasonGCWorkerIdle, traceBlockSystemGoroutine, 0) |
| |
| |
| |
| |
| systemstack(func() { |
| casGToWaitingForGC(gp, _Grunning, waitReasonGCWorkerActive) |
| |
| |
| casgstatus(gp, _Gwaiting, _Grunning) |
| }) |
| |
| |
| |
| } |
| } |
stopTheWorldWithSema
停止所有用户的 goroutine 做一些事情
| func stopTheWorldWithSema(reason stwReason) worldStop { |
| |
| |
| lock(&sched.lock) |
| preemptall() |
| |
| gp.m.p.ptr().status = _Pgcstop |
| gp.m.p.ptr().gcStopTime = start |
| sched.stopwait-- |
| trace = traceAcquire() |
| for _, pp := range allp { |
| s := pp.status |
| if s == _Psyscall && atomic.Cas(&pp.status, s, _Pgcstop) { |
| if trace.ok() { |
| trace.ProcSteal(pp, false) |
| } |
| pp.syscalltick++ |
| pp.gcStopTime = nanotime() |
| sched.stopwait-- |
| } |
| } |
| if trace.ok() { |
| traceRelease(trace) |
| } |
| now := nanotime() |
| for { |
| pp, _ := pidleget(now) |
| if pp == nil { |
| break |
| } |
| pp.status = _Pgcstop |
| pp.gcStopTime = nanotime() |
| sched.stopwait-- |
| } |
| wait := sched.stopwait > 0 |
| unlock(&sched.lock) |
| |
| |
| if wait { |
| for { |
| if notetsleep(&sched.stopnote, 100*1000) { |
| noteclear(&sched.stopnote) |
| break |
| } |
| preemptall() |
| } |
| } |
| |
| |
| |
| worldStopped() |
| |
| return worldStop{ |
| reason: reason, |
| startedStopping: start, |
| finishedStopping: finish, |
| stoppingCPUTime: stoppingCPUTime, |
| } |
| } |
startCycle
设置可以使用多少个核心进行 GC
| func (c *gcControllerState) startCycle(markStartTime int64, procs int, trigger gcTrigger) { |
| |
| totalUtilizationGoal := float64(procs) * gcBackgroundUtilization |
| dedicatedMarkWorkersNeeded := int64(totalUtilizationGoal + 0.5) |
| utilError := float64(dedicatedMarkWorkersNeeded)/totalUtilizationGoal - 1 |
| const maxUtilError = 0.3 |
| |
| if utilError < -maxUtilError || utilError > maxUtilError { |
| if float64(dedicatedMarkWorkersNeeded) > totalUtilizationGoal { |
| dedicatedMarkWorkersNeeded-- |
| } |
| c.fractionalUtilizationGoal = (totalUtilizationGoal - float64(dedicatedMarkWorkersNeeded)) / float64(procs) |
| } else { |
| c.fractionalUtilizationGoal = 0 |
| } |
| |
| if debug.gcstoptheworld > 0 { |
| dedicatedMarkWorkersNeeded = int64(procs) |
| c.fractionalUtilizationGoal = 0 |
| } |
| |
| |
| } |
setGCPhase
开启混合写屏障
| func setGCPhase(x uint32) { |
| atomic.Store(&gcphase, x) |
| writeBarrier.enabled = gcphase == _GCmark || gcphase == _GCmarktermination |
| } |
| TEXT gcWriteBarrier<>(SB),NOSPLIT,$112 |
| // ... |
| CALL runtime·wbBufFlush(SB) |
| // ... |
| func wbBufFlush() { |
| |
| if getg().m.dying > 0 { |
| getg().m.p.ptr().wbBuf.discard() |
| return |
| } |
| |
| systemstack(func() { |
| wbBufFlush1(getg().m.p.ptr()) |
| }) |
| } |
| |
| func wbBufFlush1(pp *p) { |
| |
| start := uintptr(unsafe.Pointer(&pp.wbBuf.buf[0])) |
| n := (pp.wbBuf.next - start) / unsafe.Sizeof(pp.wbBuf.buf[0]) |
| ptrs := pp.wbBuf.buf[:n] |
| |
| |
| |
| pp.wbBuf.next = 0 |
| |
| if useCheckmark { |
| |
| for _, ptr := range ptrs { |
| shade(ptr) |
| } |
| pp.wbBuf.reset() |
| return |
| } |
| |
| |
| |
| gcw := &pp.gcw |
| pos := 0 |
| for _, ptr := range ptrs { |
| if ptr < minLegalPointer { |
| continue |
| } |
| obj, span, objIndex := findObject(ptr, 0, 0) |
| if obj == 0 { |
| continue |
| } |
| mbits := span.markBitsForIndex(objIndex) |
| if mbits.isMarked() { |
| continue |
| } |
| mbits.setMarked() |
| arena, pageIdx, pageMask := pageIndexOf(span.base()) |
| if arena.pageMarks[pageIdx]&pageMask == 0 { |
| atomic.Or8(&arena.pageMarks[pageIdx], pageMask) |
| } |
| |
| if span.spanclass.noscan() { |
| gcw.bytesMarked += uint64(span.elemsize) |
| continue |
| } |
| ptrs[pos] = obj |
| pos++ |
| } |
| |
| |
| gcw.putBatch(ptrs[:pos]) |
| |
| pp.wbBuf.reset() |
| } |
| |
gcMarkTinyAllocs
把所有的 tinyalloc 标记为灰色
| func gcMarkTinyAllocs() { |
| assertWorldStopped() |
| |
| for _, p := range allp { |
| c := p.mcache |
| if c == nil || c.tiny == 0 { |
| continue |
| } |
| _, span, objIndex := findObject(c.tiny, 0, 0) |
| gcw := &p.gcw |
| greyobject(c.tiny, 0, 0, span, gcw, objIndex) |
| } |
| } |
startTheWorldWithSema
| func startTheWorldWithSema(now int64, w worldStop) int64 { |
| |
| |
| worldStarted() |
| |
| |
| for p1 != nil { |
| p := p1 |
| p1 = p1.link.ptr() |
| if p.m != 0 { |
| mp := p.m.ptr() |
| p.m = 0 |
| if mp.nextp != 0 { |
| throw("startTheWorld: inconsistent mp->nextp") |
| } |
| mp.nextp.set(p) |
| notewakeup(&mp.park) |
| } else { |
| newm(nil, p, -1) |
| } |
| } |
| |
| |
| |
| return now |
| } |
| |
worker 标记
在 goroutine 调度中会执行 findRunnableGCWorker
goroutine 这个就是启动标记 worker 的函数
| func (c *gcControllerState) findRunnableGCWorker(pp *p, now int64) (*g, int64) { |
| |
| |
| |
| node := (*gcBgMarkWorkerNode)(gcBgMarkWorkerPool.pop()) |
| |
| casgstatus(gp, _Gwaiting, _Grunnable) |
| |
| return gp, now |
| } |
gcDrain
gcDrain 是标记的主流程
| func gcDrain(gcw *gcWork, flags gcDrainFlags) { |
| |
| |
| |
| if work.markrootNext < work.markrootJobs { |
| for !(gp.preempt && (preemptible || sched.gcwaiting.Load() || pp.runSafePointFn != 0)) { |
| job := atomic.Xadd(&work.markrootNext, +1) - 1 |
| if job >= work.markrootJobs { |
| break |
| } |
| markroot(gcw, job, flushBgCredit) |
| if check != nil && check() { |
| goto done |
| } |
| } |
| } |
| |
| for !(gp.preempt && (preemptible || sched.gcwaiting.Load() || pp.runSafePointFn != 0)) { |
| if work.full == 0 { |
| gcw.balance() |
| } |
| |
| |
| b := gcw.tryGetFast() |
| if b == 0 { |
| |
| b = gcw.tryGet() |
| if b == 0 { |
| |
| wbBufFlush() |
| b = gcw.tryGet() |
| } |
| } |
| if b == 0 { |
| break |
| } |
| |
| scanobject(b, gcw) |
| |
| |
| } |
| |
| done: |
| |
| if gcw.heapScanWork > 0 { |
| gcController.heapScanWork.Add(gcw.heapScanWork) |
| if flushBgCredit { |
| gcFlushBgCredit(gcw.heapScanWork - initScanWork) |
| } |
| gcw.heapScanWork = 0 |
| } |
| } |
scanobject
| func scanobject(b uintptr, gcw *gcWork) { |
| var tp typePointers |
| if n > maxObletBytes { |
| |
| if b == s.base() { |
| for oblet := b + maxObletBytes; oblet < s.base()+s.elemsize; oblet += maxObletBytes { |
| if !gcw.putFast(oblet) { |
| gcw.put(oblet) |
| } |
| } |
| n = s.base() + s.elemsize - b |
| n = min(n, maxObletBytes) |
| tp = s.typePointersOfUnchecked(s.base()) |
| tp = tp.fastForward(b-tp.addr, b+n) |
| } else { |
| tp = s.typePointersOfUnchecked(b) |
| } |
| |
| var scanSize uintptr |
| for { |
| var addr uintptr |
| if tp, addr = tp.nextFast(); addr == 0 { |
| if tp, addr = tp.next(b + n); addr == 0 { |
| break |
| } |
| } |
| |
| scanSize = addr - b + goarch.PtrSize |
| |
| obj := *(*uintptr)(unsafe.Pointer(addr)) |
| |
| if obj != 0 && obj-b >= n { |
| if obj, span, objIndex := findObject(obj, b, addr-b); obj != 0 { |
| |
| greyobject(obj, b, addr-b, span, gcw, objIndex) |
| } |
| } |
| } |
| } |
| } |
| |
greyobject
gcmarkBits 是一个 bitmaps 代表了一个对象是不是可用(gc 过程中)
- 如果 Bit = 1 对象不在 gcw 中 黑色
- 如果 Bit = 0 对象不在 gcw 中 白色
- 如果对象在 gcw 中 灰色
| func greyobject(obj, base, off uintptr, span *mspan, gcw *gcWork, objIndex uintptr) { |
| |
| mbits := span.markBitsForIndex(objIndex) |
| if useCheckmark { |
| } else { |
| mbits.setMarked() |
| } |
| |
| |
| sys.Prefetch(obj) |
| if !gcw.putFast(obj) { |
| gcw.put(obj) |
| } |
| } |
| |
| func (s *mspan) markBitsForIndex(objIndex uintptr) markBits { |
| bytep, mask := s.gcmarkBits.bitp(objIndex) |
| return markBits{bytep, mask, objIndex} |
| } |
| |
| func (m markBits) setMarked() { |
| atomic.Or8(m.bytep, m.mask) |
| } |
用户 goroutine 清扫
| func deductAssistCredit(size uintptr) *g { |
| var assistG *g |
| if gcBlackenEnabled != 0 { |
| assistG = getg() |
| if assistG.m.curg != nil { |
| assistG = assistG.m.curg |
| } |
| assistG.gcAssistBytes -= int64(size) |
| |
| if assistG.gcAssistBytes < 0 { |
| gcAssistAlloc(assistG) |
| } |
| } |
| return assistG |
| } |
| func gcAssistAlloc(gp *g) { |
| |
| retry: |
| |
| |
| |
| systemstack(func() { |
| gcAssistAlloc1(gp, scanWork) |
| }) |
| |
| |
| } |
| func gcAssistAlloc1(gp *g, scanWork int64) { |
| |
| gcw := &getg().m.p.ptr().gcw |
| workDone := gcDrainN(gcw, scanWork) |
| |
| casgstatus(gp, _Gwaiting, _Grunning) |
| |
| } |
终止标记 gcMarkDone
在 gcBgMarkStartWorkers 中执行 gcMarkDone 终止
| func gcMarkDone() { |
| |
| |
| top: |
| |
| gcMarkDoneFlushed = 0 |
| forEachP(waitReasonGCMarkTermination, func(pp *p) { |
| wbBufFlush1(pp) |
| |
| pp.gcw.dispose() |
| if pp.gcw.flushedWork { |
| atomic.Xadd(&gcMarkDoneFlushed, 1) |
| pp.gcw.flushedWork = false |
| } |
| }) |
| |
| if gcMarkDoneFlushed != 0 { |
| |
| semrelease(&worldsema) |
| goto top |
| } |
| |
| |
| systemstack(func() { |
| stw = stopTheWorldWithSema(stwGCMarkTerm) |
| }) |
| |
| restart := false |
| systemstack(func() { |
| for _, p := range allp { |
| wbBufFlush1(p) |
| if !p.gcw.empty() { |
| restart = true |
| break |
| } |
| } |
| }) |
| if restart { |
| getg().m.preemptoff = "" |
| systemstack(func() { |
| work.cpuStats.accumulateGCPauseTime(nanotime()-stw.finishedStopping, work.maxprocs) |
| |
| now := startTheWorldWithSema(0, stw) |
| work.pauseNS += now - stw.startedStopping |
| }) |
| semrelease(&worldsema) |
| goto top |
| } |
| |
| |
| gcMarkTermination(stw) |
| } |
gcMarkTermination
| func gcMarkTermination(stw worldStop) { |
| |
| setGCPhase(_GCmarktermination) |
| |
| |
| casGToWaitingForGC(curgp, _Grunning, waitReasonGarbageCollection) |
| |
| |
| |
| |
| systemstack(func() { |
| gcMark(startTime) |
| }) |
| |
| var stwSwept bool |
| systemstack(func() { |
| work.heap2 = work.bytesMarked |
| if debug.gccheckmark > 0 { |
| |
| |
| |
| |
| startCheckmarks() |
| gcResetMarkState() |
| gcw := &getg().m.p.ptr().gcw |
| gcDrain(gcw, 0) |
| wbBufFlush1(getg().m.p.ptr()) |
| gcw.dispose() |
| endCheckmarks() |
| } |
| |
| |
| setGCPhase(_GCoff) |
| |
| stwSwept = gcSweep(work.mode) |
| }) |
| |
| casgstatus(curgp, _Gwaiting, _Grunning) |
| |
| |
| |
| systemstack(func() { |
| |
| startTheWorldWithSema(now, stw) |
| }) |
| |
| |
| |
| |
| forEachP(waitReasonFlushProcCaches, func(pp *p) { |
| pp.mcache.prepareForSweep() |
| if pp.status == _Pidle { |
| systemstack(func() { |
| lock(&mheap_.lock) |
| pp.pcache.flush(&mheap_.pages) |
| unlock(&mheap_.lock) |
| }) |
| } |
| pp.pinnerCache = nil |
| }) |
| if sl.valid { |
| |
| |
| |
| |
| |
| |
| sweep.active.end(sl) |
| } |
| |
| } |
清扫
| func gcSweep(mode gcMode) bool { |
| |
| |
| |
| lock(&sweep.lock) |
| if sweep.parked { |
| sweep.parked = false |
| ready(sweep.g, 0, true) |
| } |
| unlock(&sweep.lock) |
| return false |
| } |
| |
sweep.g 的创建
| var sweep sweepdata |
| |
| type sweepdata struct { |
| g *g |
| } |
| |
| |
| func gcenable() { |
| c := make(chan int, 2) |
| go bgsweep(c) |
| go bgscavenge(c) |
| <-c |
| <-c |
| memstats.enablegc = true |
| } |
bgscavenge
从 OS 申请到 _heap 上的内存 不用了 还回去一些
| func bgscavenge(c chan int) { |
| scavenger.init() |
| |
| c <- 1 |
| |
| scavenger.park() |
| |
| for { |
| |
| released, workTime := scavenger.run() |
| |
| if released == 0 { |
| scavenger.park() |
| continue |
| } |
| |
| mheap_.pages.scav.releasedBg.Add(released) |
| scavenger.sleep(workTime) |
| } |
| } |
scavenger.run
| func (s *scavengerState) run() (released uintptr, worked float64) { |
| for worked < minScavWorkTime { |
| |
| if s.shouldStop() { |
| break |
| } |
| |
| const scavengeQuantum = 64 << 10 |
| r, duration := s.scavenge(scavengeQuantum) |
| |
| const approxWorkedNSPerPhysicalPage = 10e3 |
| if duration == 0 { |
| worked += approxWorkedNSPerPhysicalPage * float64(r/physPageSize) |
| } else { |
| worked += float64(duration) |
| } |
| released += r |
| |
| } |
| |
| return |
| } |
s.scavenge
| if s.scavenge == nil { |
| s.scavenge = func(n uintptr) (uintptr, int64) { |
| start := nanotime() |
| r := mheap_.pages.scavenge(n, nil, false) |
| end := nanotime() |
| if start >= end { |
| return r, 0 |
| } |
| scavenge.backgroundTime.Add(end - start) |
| return r, end - start |
| } |
| } |
| func (p *pageAlloc) scavenge(nbytes uintptr, shouldStop func() bool, force bool) uintptr { |
| released := uintptr(0) |
| for released < nbytes { |
| ci, pageIdx := p.scav.index.find(force) |
| if ci == 0 { |
| break |
| } |
| systemstack(func() { |
| released += p.scavengeOne(ci, pageIdx, nbytes-released) |
| }) |
| if shouldStop != nil && shouldStop() { |
| break |
| } |
| } |
| return released |
| } |
scavengeOne
| func (p *pageAlloc) scavengeOne(ci chunkIdx, searchIdx uint, max uintptr) uintptr { |
| maxPages := max / pageSize |
| if max%pageSize != 0 { |
| maxPages++ |
| } |
| |
| minPages := physPageSize / pageSize |
| if minPages < 1 { |
| minPages = 1 |
| } |
| |
| lock(p.mheapLock) |
| if p.summary[len(p.summary)-1][ci].max() >= uint(minPages) { |
| |
| base, npages := p.chunkOf(ci).findScavengeCandidate(searchIdx, minPages, maxPages) |
| |
| |
| if npages != 0 { |
| |
| unlock(p.mheapLock) |
| |
| if !p.test { |
| |
| sysUnused(unsafe.Pointer(addr), uintptr(npages)*pageSize) |
| |
| |
| } |
| |
| |
| lock(p.mheapLock) |
| if b := (offAddr{addr}); b.lessThan(p.searchAddr) { |
| p.searchAddr = b |
| } |
| p.chunkOf(ci).free(base, npages) |
| p.update(addr, uintptr(npages), true, false) |
| |
| |
| p.chunkOf(ci).scavenged.setRange(base, npages) |
| unlock(p.mheapLock) |
| |
| return uintptr(npages) * pageSize |
| } |
| } |
| p.scav.index.setEmpty(ci) |
| unlock(p.mheapLock) |
| |
| return 0 |
| } |
bgsweep
| func bgsweep(c chan int) { |
| sweep.g = getg() |
| |
| lockInit(&sweep.lock, lockRankSweep) |
| lock(&sweep.lock) |
| sweep.parked = true |
| c <- 1 |
| |
| goparkunlock(&sweep.lock, waitReasonGCSweepWait, traceBlockGCSweep, 1) |
| |
| for { |
| |
| for sweepone() != ^uintptr(0) { |
| nSwept++ |
| |
| if nSwept%sweepBatchSize == 0 { |
| goschedIfBusy() |
| } |
| } |
| |
| for freeSomeWbufs(true) { |
| goschedIfBusy() |
| } |
| lock(&sweep.lock) |
| |
| if !isSweepDone() { |
| unlock(&sweep.lock) |
| continue |
| } |
| sweep.parked = true |
| |
| goparkunlock(&sweep.lock, waitReasonGCSweepWait, traceBlockGCSweep, 1) |
| } |
| } |
sweepone
| func sweepone() uintptr { |
| gp := getg() |
| gp.m.locks++ |
| sl := sweep.active.begin() |
| if !sl.valid { |
| gp.m.locks-- |
| return ^uintptr(0) |
| } |
| |
| |
| npages := ^uintptr(0) |
| var noMoreWork bool |
| for { |
| |
| s := mheap_.nextSpanForSweep() |
| if s == nil { |
| noMoreWork = sweep.active.markDrained() |
| break |
| } |
| |
| if s, ok := sl.tryAcquire(s); ok { |
| |
| npages = s.npages |
| if s.sweep(false) { |
| mheap_.reclaimCredit.Add(npages) |
| } else { |
| npages = 0 |
| } |
| break |
| } |
| } |
| sweep.active.end(sl) |
| |
| |
| gp.m.locks-- |
| return npages |
| } |
sweep 主要逻辑是把 刚刚标记过的 gcmarkBits 复制给 allocBits
| func (sl *sweepLocked) sweep(preserve bool) bool { |
| |
| s.allocBits = s.gcmarkBits |
| s.gcmarkBits = newMarkBits(uintptr(s.nelems)) |
| |
| } |
分配内存
如果我们的 span 标记之后 还没来得及清扫,修改了 allocBits 值,然后再清扫会出问题。go 是怎么解决的呢?
| |
| func (c *mcentral) cacheSpan() *mspan { |
| |
| if s, ok := sl.tryAcquire(s); ok { |
| |
| s.sweep(true) |
| } |
| |
| } |
| |
| func (h *mheap) alloc(npages uintptr, spanclass spanClass) *mspan { |
| var s *mspan |
| systemstack(func() { |
| if !isSweepDone() { |
| h.reclaim(npages) |
| } |
| s = h.allocSpan(npages, spanAllocHeap, spanclass) |
| }) |
| return s |
| } |
| |
| func (h *mheap) reclaim(npage uintptr) { |
| |
| nfound := h.reclaimChunk(arenas, idx, pagesPerReclaimerChunk) |
| |
| } |
| |
| func (h *mheap) reclaimChunk(arenas []*pageAlloc, idx, npages uintptr) uintptr { |
| |
| if s.sweep(false) { |
| } |
| |
| } |
| |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
2021-12-20 vscode常用快捷键及插件