返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

3.5 清理

发布于 2024-10-12 19:15:57 字数 4344 浏览 0 评论 0 收藏 0

默认情况下,采取后台并发模式执行清理操作。

// mgc.go

func gcSweep(mode gcMode) {
    
    // STW !!!
	assertWorldStopped()

	if gcphase != _GCoff {
		throw("gcSweep being done but phase is not GCoff")
	}

    // 累加代龄,重置状态。
	mheap_.sweepgen += 2
	mheap_.pagesSwept.Store(0)

    // 同步阻塞模式。
	if !_ConcurrentSweep || mode == gcForceBlockMode {
        
		// 清理所有 spans。
		for sweepone() != ^uintptr(0) {
			sweep.npausesweep++
		}
        
		// 释放队列。
		prepareFreeWorkbufs()
		for freeSomeWbufs(false) {
		}
        
		return
	}

	// 并发后台清理。
	if sweep.parked {
		sweep.parked = false
		ready(sweep.g, 0, true)
	}
}

并发清理同样使用专门的 goroutine 完成。

// proc.go, mgc.go

// The main goroutine.
func main() {
    gcenable()
}

// It kicks off the background sweeper goroutine, the background
// scavenger goroutine, and enables GC.
func gcenable() {
    go bgsweep(c)
}
// mgcsweep.go

func bgsweep(c chan int) {
    
    // 全局变量,存储清理状态。
	sweep.g = getg()
	sweep.parked = true
    
    // 休眠。
	goparkunlock(&sweep.lock, waitReasonGCSweepWait, traceEvGoBlock, 1)

	for {
        
        // 清理所有 spans。
		for sweepone() != ^uintptr(0) {
			sweep.nbgsweep++
			Gosched()
		}
        
        // 释放队列。
		for freeSomeWbufs(true) {
			Gosched()
		}
        
        // 检查是否有遗漏未清理。
		if !isSweepDone() {
			// This can happen if a GC runs between
			// gosweepone returning ^0 above
			// and the lock being acquired.
			continue
		}
        
        // 休眠,等待下个清理周期。
		sweep.parked = true
		goparkunlock(&sweep.lock, waitReasonGCSweepWait, traceEvGoBlock, 1)
	}
}
// mgcsweep.go

var sweep sweepdata

// State of background sweep.
type sweepdata struct {
	g       *g
	parked  bool
	started bool
    
	// centralIndex is the current unswept span class.
	// It represents an index into the mcentral span
	// sets. Accessed and updated via its load and
	// update methods. Not protected by a lock.
	//
	// Reset at mark termination.
	// Used by mheap.nextSpanForSweep.
	centralIndex sweepClass    
}

遍历所有使用中的 mspan,按标记结果进行清理。

有关 mspan.sweep,请阅读《内存分配器:2.4 回收》。

// mgcsweep.go

// sweepone sweeps some unswept heap span and returns the number of pages returned
// to the heap, or ^uintptr(0) if there was nothing to sweep.
func sweepone() uintptr {

	npages := ^uintptr(0)
    
    // 遍历 mheap.central[] 内所有 mspan。
	for {
		s := mheap_.nextSpanForSweep()
        
		if s == nil {
			noMoreWork = sweep.active.markDrained()            
			break
		}
        
		if state := s.state.get(); state != mSpanInUse {
			continue
		}
        
		// 清理。
		npages = s.npages
		if s.sweep(false) {
			// Whole span was freed. 
		} else {
			// Span is still in-use, so this returned no
			// pages to the heap and the span needs to
			// move to the swept in-use list.
			npages = 0
		}
		break
	}

    // 清理列表已空,尝试释放物理内存。
	if noMoreWork {
		readyForScavenger()
	}

	return npages
}
func (h *mheap) nextSpanForSweep() *mspan {
	sg := h.sweepgen
    
    // 按 sizeclass 遍历 mheap.central[] 数组。
	for sc := sweep.centralIndex.load(); sc < numSweepClasses; sc++ {
        
		spc, full := sc.split()
		c := &h.central[spc].mcentral
        
        // 分别检查 full 和 partial 列表。
		var s *mspan
		if full {
			s = c.fullUnswept(sg).pop()
		} else {
			s = c.partialUnswept(sg).pop()
		}
        
        // 记下本次 sizeclass,作为下次调用起点。
		if s != nil {
			// Write down that we found something so future sweepers
			// can start from here.
			sweep.centralIndex.update(sc)
			return s
		}
	}
    
	// Write down that we found nothing.
	sweep.centralIndex.update(sweepClassDone)
	return nil
}

代龄

不同于其他语言里描述对象存活周期的代龄概念,此代龄表示 mspan 清理状态。

// mheap.go

type mheap struct {
	sweepgen uint32 // sweep generation
}

type mspan struct {
    
	// sweep generation:
	// if sweepgen == h->sweepgen - 2, the span needs sweeping
	// if sweepgen == h->sweepgen - 1, the span is currently being swept
	// if sweepgen == h->sweepgen, the span is swept and ready to use
	// if sweepgen == h->sweepgen + 1, the span was cached before sweep began and is still cached, and needs sweeping
	// if sweepgen == h->sweepgen + 3, the span was swept and then cached and is still cached
	// h->sweepgen is incremented by 2 after every GC

	sweepgen    uint32    
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文