返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

3.4.3 辅助

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

为避免回收期间,并发执行的用户代码(mutator)大量分配内存,故引入信用额度进行限制。

// runtime2.go

type g struct {
    
	// gcAssistBytes is this G's GC assist credit in terms of
	// bytes allocated. If this is positive, then the G has credit
	// to allocate gcAssistBytes bytes without assisting. If this
	// is negative, then the G must correct this by performing
	// scan work. We track this in bytes to make it fast to update
	// and check for debt in the malloc hot path. The assist ratio
	// determines how this corresponds to scan work debt.
	gcAssistBytes int64
    
}
// malloc.go

func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
    
	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)
		}
	}
    
}

欠债人没有立即参与辅助回收,试图从公共账户(gcController.bgScanCredit)偷钱还债。

// mgcmark.go

// gcAssistAlloc performs GC work to make gp's assist debt positive.
func gcAssistAlloc(gp *g) {
    
retry:
    
    // 计算偿还债务所需的工作量。
	assistWorkPerByte := gcController.assistWorkPerByte.Load()
	assistBytesPerWork := gcController.assistBytesPerWork.Load()
	debtBytes := -gp.gcAssistBytes
	scanWork := int64(assistWorkPerByte * float64(debtBytes))
    
    // 工作量不能少于 64 KB。
	if scanWork < gcOverAssistWork {
		scanWork = gcOverAssistWork
		debtBytes = int64(assistBytesPerWork * float64(scanWork))
	}

    // 尝试从公共账号偷窃,偿还部分或全部债务。
	bgScanCredit := atomic.Loadint64(&gcController.bgScanCredit)
	stolen := int64(0)
	if bgScanCredit > 0 {
		if bgScanCredit < scanWork {
			stolen = bgScanCredit
			gp.gcAssistBytes += 1 + int64(assistBytesPerWork*float64(stolen))
		} else {
			stolen = scanWork
			gp.gcAssistBytes += debtBytes
		}
		atomic.Xaddint64(&gcController.bgScanCredit, -stolen)

        // 剩余工作量。如债务全部还清,就不去打工了。
		scanWork -= stolen
		if scanWork == 0 {
			// We were able to steal all of the credit we
			// needed.
			return
		}
	}

	// 开始辅助回收。
	systemstack(func() {
		gcAssistAlloc1(gp, scanWork)
	})

    // 整个标记任务结束了。
	completed := gp.param != nil
	gp.param = nil
	if completed {
		gcMarkDone()
	}

    // 赚的钱不够还债,还需做点什么。
	if gp.gcAssistBytes < 0 {
        
		if gp.preempt {
			Gosched()
			goto retry
		}

        // 放入专门队列(work.assistQueue),休眠。
        // 直到公共账号有钱时被唤醒。
		if !gcParkAssist() {
			goto retry
		}
	}
}
// mgcmark.go

func gcAssistAlloc1(gp *g, scanWork int64) {
    
    // 用于存储标记阶段结束。
	gp.param = nil

    // 垃圾回收周期结束,债务清零。
	if atomic.Load(&gcBlackenEnabled) == 0 {
		gp.gcAssistBytes = 0
		return
	}

    // 递减等待人数。
	decnwait := atomic.Xadd(&work.nwait, -1)

    // 修改状态。
	casgstatus(gp, _Grunning, _Gwaiting)
	gp.waitreason = waitReasonGCAssistMarking

    // 开始工作。
	gcw := &getg().m.p.ptr().gcw
	workDone := gcDrainN(gcw, scanWork)   // 指定工作量。
	casgstatus(gp, _Gwaiting, _Grunning)

    // 将劳动所得存入个人账户。
	assistBytesPerWork := gcController.assistBytesPerWork.Load()
	gp.gcAssistBytes += 1 + int64(assistBytesPerWork*float64(workDone))

	// 工作结束,递增等待人数。
    // 如果工人都在等待,且没有可用任务,表示整个标记任务结束。
	incnwait := atomic.Xadd(&work.nwait, +1)
	if incnwait == work.nproc && !gcMarkWorkAvailable(nil) {
		gp.param = unsafe.Pointer(gp)  // 结束标记。
	}
}

公共账户

工人将劳动所得存入公共账号。为欠债休眠的 G 偿还债务,唤醒它们继续工作。

// mgcmark.go

func gcDrain(gcw *gcWork, flags gcDrainFlags) {

    flushBgCredit := flags&gcDrainFlushBgCredit != 0

	// Drain root marking jobs ...
	// Drain heap marking jobs ...
    
	// Flush background scan work credit to the global
	// account if we've accumulated enough locally so
	// mutator assists can draw on it.
	if gcw.heapScanWork >= gcCreditSlack {
		if flushBgCredit {
			gcFlushBgCredit(gcw.heapScanWork - initScanWork)
		}
	}

done:
	// Flush remaining scan work credit.
	if gcw.heapScanWork > 0 {
		if flushBgCredit {
			gcFlushBgCredit(gcw.heapScanWork - initScanWork)
		}
	}
}
// gcFlushBgCredit flushes scanWork units of background scan work
// credit. This first satisfies blocked assists on the
// work.assistQueue and then flushes any remaining credit to
// gcController.bgScanCredit.

func gcFlushBgCredit(scanWork int64) {
    
    // 如队列中没有辅助 G 休眠,存款后退出。
	if work.assistQueue.q.empty() {
		atomic.Xaddint64(&gcController.bgScanCredit, scanWork)
		return
	}

	assistBytesPerWork := gcController.assistBytesPerWork.Load()
	scanBytes := int64(float64(scanWork) * assistBytesPerWork)

	// 遍历休眠队列。
	for !work.assistQueue.q.empty() && scanBytes > 0 {
        
        // 找出一个休眠者。
		gp := work.assistQueue.q.pop()
        
        // 如能替它偿还全部债务。
		if scanBytes+gp.gcAssistBytes >= 0 {
            
            // 偿还债务,唤醒。
			scanBytes += gp.gcAssistBytes
			gp.gcAssistBytes = 0
			ready(gp, 0, false)
            
		} else {
            
            // 不够,偿还部分债务。放回休眠队列。
			gp.gcAssistBytes += scanBytes
			scanBytes = 0
			work.assistQueue.q.pushBack(gp)
            
            // 没有余款,退出循环。
			break
		}
	}

    // 尚有余款,存入公共账户。
	if scanBytes > 0 {
		assistWorkPerByte := gcController.assistWorkPerByte.Load()
		scanWork = int64(float64(scanBytes) * assistWorkPerByte)
		atomic.Xaddint64(&gcController.bgScanCredit, scanWork)
	}
}

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

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

发布评论

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