美文网首页
go gc 源码分析

go gc 源码分析

作者: wwq2020 | 来源:发表于2020-08-05 15:50 被阅读0次

简介

go 内存分配
1 检查GC是否在工作中,如果是而且当前G分配了内存则协助GC做工作
这个机制叫GC Assist, 用于防止分配内存太快导致GC回收跟不上的情况发生.
2从mcache取,本地,无锁
3从mcentral取,全局,需要加锁
4从mheap取,全局,需要加锁

go 进行堆内存分配时,会做相应的检查,如果满足gc条件,则
1 stop the world
2 等待 sweep 结束
3 设置 gc 阶段为 \_GCmark,启动写屏障,辅助 GC,添加根标记任务
4 start the world
5 开始并发标记,从根对象查找可到的对象,标记为灰,并添加到任务队列,如果灰色对象有引用其他对象,则自身标记为黑,引用对象标记为灰色
6 stop the world
7 设置 gc 阶段为 \_GCmarktermination,禁止 workers 和 assists,flushing mcaches,设置 spans 为需要清理
8 设置 gc 阶段为 \_GCoff,设置 sweep 状态和禁用写屏障
9 start the world
10 开启后台并发清除

所以go的标记和清除是不需要stop the world的

go 堆上内存分配是通过 newobject 来分配的

runtime/malloc.go 中

func newobject(typ *_type) unsafe.Pointer {
    return mallocgc(typ.size, typ, true)
}

func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
    ...
    var assistG *g
    if gcBlackenEnabled != 0 {
        // Charge the current user G for this allocation.
        assistG = getg()
        if assistG.m.curg != nil {
            assistG = assistG.m.curg
        }
        // Charge the allocation against the G. We'll account
        // for internal fragmentation at the end of mallocgc.
        assistG.gcAssistBytes -= int64(size)

        if assistG.gcAssistBytes < 0 {
            // This G is in debt. Assist the GC to correct
            // this before allocating. This must happen
            // before disabling preemption.
            gcAssistAlloc(assistG)
        }
    }
    ...
    // 32k
    if size <= maxSmallSize {
        ...
        spc := makeSpanClass(sizeclass, noscan)
        span := c.alloc[spc]
        v := nextFreeFast(span)
        if v == 0 {
            v, span, shouldhelpgc = c.nextFree(spc)
        }
        ...
    } else {
        var s *mspan
        shouldhelpgc = true
        systemstack(func() {
            s = largeAlloc(size, needzero, noscan)
        })
    }

    ...
    publicationBarrier()

    // Allocate black during GC.
    // All slots hold nil so no scanning is needed.
    // This may be racing with GC so do it atomically if there can be
    // a race marking the bit.
    if gcphase != _GCoff {
        gcmarknewobject(uintptr(x), size, scanSize)
    }
    ...
    if shouldhelpgc {
        if t := (gcTrigger{kind: gcTriggerHeap}); t.test() {
            gcStart(t)
        }
    }
    ...
}

runtime/mgc.go 中


func (t gcTrigger) test() bool {
    if !memstats.enablegc || panicking != 0 || gcphase != _GCoff {
        return false
    }
    switch t.kind {
    case gcTriggerHeap:
        // Non-atomic access to heap_live for performance. If
        // we are going to trigger on this, this thread just
        // atomically wrote heap_live anyway and we'll see our
        // own write.
        return memstats.heap_live >= memstats.gc_trigger
    case gcTriggerTime:
        if gcpercent < 0 {
            return false
        }
        lastgc := int64(atomic.Load64(&memstats.last_gc_nanotime))
        return lastgc != 0 && t.now-lastgc > forcegcperiod
    case gcTriggerCycle:
        // t.n > work.cycles, but accounting for wraparound.
        return int32(t.n-work.cycles) > 0
    }
    return true
}


func gcStart(trigger gcTrigger) {
    ...
    gcBgMarkStartWorkers()
    ...
    systemstack(func() {
        finishsweep_m()
    })
    clearpools()
    ...
    // In STW mode, disable scheduling of user Gs. This may also
    // disable scheduling of this goroutine, so it may block as
    // soon as we start the world again.
    if mode != gcBackgroundMode {
        schedEnableUser(false)
    }
    setGCPhase(_GCmark)
    gcBgMarkPrepare() // Must happen before assist enable.
    gcMarkRootPrepare()

    // Mark all active tinyalloc blocks. Since we're
    // allocating from these, they need to be black like
    // other allocations. The alternative is to blacken
    // the tiny block on every allocation from it, which
    // would slow down the tiny allocator.
    gcMarkTinyAllocs()

    // Concurrent mark.
    systemstack(func() {
        now = startTheWorldWithSema(trace.enabled)
        work.pauseNS += now - work.pauseStart
        work.tMark = now
    })
    if mode != gcBackgroundMode {
        Gosched()
    }
}

func finishsweep_m() {
    // Sweeping must be complete before marking commences, so
    // sweep any unswept spans. If this is a concurrent GC, there
    // shouldn't be any spans left to sweep, so this should finish
    // instantly. If GC was forced before the concurrent sweep
    // finished, there may be spans to sweep.
    for sweepone() != ^uintptr(0) {
        sweep.npausesweep++
    }

    nextMarkBitArenaEpoch()
}

func gcBgMarkWorker(_p_ *p) {
    ...
    gcDrain(&_p_.gcw, gcDrainFlushBgCredit)
    ...
    gcMarkDone()
    ...
}

func gcMarkDone() {
    ...
    gcMarkTermination()
    ...
}

func gcMarkTermination(nextTriggerRatio float64) {
    ...
    gcSweep(work.mode)
    ...
}


func gcSweep(mode gcMode) {
    ...
    lock(&sweep.lock)
    if sweep.parked {
        sweep.parked = false
        ready(sweep.g, 0, true)
    }
    unlock(&sweep.lock)
    ...
}

func gcenable() {
    // Kick off sweeping and scavenging.
    c := make(chan int, 2)
    go bgsweep(c)
    go bgscavenge(c)
    <-c
    <-c
    memstats.enablegc = true // now that runtime is initialized, GC is okay
}


runtime/mgcmark.go 中

func markroot(gcw *gcWork, i uint32) {
    ...
    scanstack(gp, gcw)
    ...
}

func scanstack(gp *g, gcw *gcWork) {
    ...
    scanblock(b, t.ptrdata, gcdata, gcw, &state)
    ...
}
func scanblock(b0, n0 uintptr, ptrmask *uint8, gcw *gcWork, stk *stackScanState) {
    ...
    if obj, span, objIndex := findObject(p, b, i); obj != 0 {
        greyobject(obj, b, i, span, gcw, objIndex)
    }
    ...
}
func gcDrain(gcw *gcWork, flags gcDrainFlags) {
    ...
    scanobject(b, gcw)
    ...
}

func scanobject(b uintptr, gcw *gcWork) {
    ...
    if obj, span, objIndex := findObject(obj, b, i); obj != 0 {
        greyobject(obj, b, i, span, gcw, objIndex)
    }
    ...
}
func main() {
    ...
    gcenable()
    ...
}

runtime/mgcsweep.go 中

func bgsweep(c chan int) {
    ...
    for {
        for sweepone() != ^uintptr(0) {
            sweep.nbgsweep++
            Gosched()
        }
        for freeSomeWbufs(true) {
            Gosched()
        }
        lock(&sweep.lock)
        if !isSweepDone() {
            // This can happen if a GC runs between
            // gosweepone returning ^0 above
            // and the lock being acquired.
            unlock(&sweep.lock)
            continue
        }
        sweep.parked = true
        goparkunlock(&sweep.lock, waitReasonGCSweepWait, traceEvGoBlock, 1)
    }
}

func sweepone() uintptr {
    s.sweep(false)
}

func (s *mspan) sweep(preserve bool) bool {
}

相关文章

  • go gc 源码分析

    简介 go 堆上内存分配是通过 newobject 来分配的 runtime/malloc.go 中 runtim...

  • Go语言——垃圾回收GC

    Go语言——垃圾回收GC 参考: Go 垃圾回收原理 Golang源码探索(三) GC的实现原理 Getting ...

  • Golang 开源项目cache2go 解读

    参考启航 - cache2go源码分析cache2go - cachetable源码分析cache2go源码最后一...

  • Golang垃圾回收gc调优

    示例代码 test.go 命令行调用go程序代码,输出gc信息到日志 golang gc 优化思路以及实例分析

  • 如何在Mac源码安装Go1.5开发环境

    到这里下载Go语言源码包。Go1.4到Go1.5是Go语言脱胎换骨的一次变更,The gc tool chain ...

  • go gc 分析

    1 先翻译一下runtime 文档中,关于gc的内容(里面涉及GC日志格式) ···原文: https://gol...

  • go-内存机制(4)

    go的GC机制 GO的GC是并行GC,也就是说GC的大部分清理和普通的go代码是同时运行的,这让GO的GC流程比较...

  • Go GC

    1、什么是GC?2、为什么会有GC?3、GC的优点?4、GC的缺点?5、Go中的GC历史6、Go中的GC实现原理(...

  • Go语言——Slice分析

    Go语言——Slice分析 源码很不好找,在go\src\runtime\slice.go。 根据容量cap*元素...

  • JVM源码解读之CMS何时会进行FullGC

    前言本文内容是基于 JDK 8 在文章 JVM 源码解读之 CMS GC 触发条件 中分析了 CMS GC 触发的...

网友评论

      本文标题:go gc 源码分析

      本文链接:https://www.haomeiwen.com/subject/jbcorktx.html