go v1.3前用stw标记-清除,v1.5起改用并发三色标记+写屏障,仅保留极短stw,标记与用户代码并行,清扫异步化。

MarkSweep 算法在 Go 中早已不是默认实现——它只存在于早期(v1.3 及之前)的运行时,现代 Go(v1.5+)实际使用的是**并发三色标记 + 写屏障 + 混合清扫**,MarkSweep 仅作为底层逻辑的简化描述存在,不能直接调用或配置。
Go 里根本没有可手动触发的 MarkSweep 函数
你找不到 runtime.MarkSweep() 或类似公开 API。Go 的 GC 是完全自动、不可插拔的运行时组件。试图“手动执行标记清除”只会误入歧途,比如:
- 调用
runtime.GC()并不等于执行一次MarkSweep,它只是强制启动一轮完整的 GC 周期(含标记、终止、清扫、归还),且仍受写屏障和并发调度约束; - 设置
GOGC=1会让 GC 频繁触发,但内部仍是三色标记流程,不是朴素的 Stop-The-World 标记-清除; - 通过
debug.SetGCPercent()调整阈值,影响的是触发时机,而非算法切换。
为什么不能回到纯 MarkSweep?STW 太长
v1.3 的 MarkSweep 要求全程 STW:从扫描根对象到清空所有未标记内存,用户 goroutine 全部挂起。实测中,堆大小达 1GB 时 STW 可达 20ms+,对延迟敏感服务不可接受。Go v1.5 引入三色标记后,STW 仅保留在两个极短点:gcStart(纳秒级)和 mark termination(毫秒级,与存活对象数相关),其余标记过程与用户代码并发。
关键区别在于:
- 纯
MarkSweep:一次全量遍历 → 全停 → 全清; - 现代 GC:灰对象队列驱动增量标记 + 写屏障拦截指针变更 + 清扫异步化 + 内存页按需归还。
runtime.ReadMemStats() 看到的 “NextGC” 和 “LastGC” 不代表 MarkSweep 阶段
这些字段反映的是整个 GC 周期的状态,不是某个子阶段。例如:
Go 是一个开源的编程语言,它能让构造简单、可靠且高效的软件变得容易。本文给大家带来Go参考手册,需要的可以来下载! Go是从2007年末由Robert Griesemer, Rob Pike, Ken Thompson主持开发,后来还加入了Ian Lance Taylor, Russ Cox等人,并最终于2009年11月开源,在2012年早些时候发布了Go 1稳定版本。现在Go的开发已经是完全开放的,并且拥有一个活跃的社区。 Go 语言特色 简洁、快速、安全 并行、有趣、开源 内存管理、v数组安全、编译
mem := &runtime.MemStats{}
runtime.ReadMemStats(mem)
fmt.Printf("next GC: %v, last GC: %v\n", time.Unix(0, int64(mem.NextGC)), time.Unix(0, int64(mem.LastGC)))
其中 NextGC 是预测下一次 GC 启动时的堆目标大小(基于当前 GOGC),LastGC 是上一轮 GC 完成的时间戳。它们不区分“标记完成时间”或“清扫结束时间”,因为清扫本身是懒执行、分批进行的——有些页可能几轮 GC 后才真正归还给 OS。
真想观察标记过程?得看 runtime/debug 和 trace
Go 不暴露标记细节,但可通过以下方式间接验证行为:
- 启用 GC trace:
GODEBUG=gctrace=1 ./yourprogram,输出如gc 3 @0.021s 0%: 0.019+0.27+0.014 ms clock,其中三项分别对应 mark setup、concurrent mark、mark termination 时间; - 用
go tool trace查看GC mark assist、GC sweep done等事件,确认清扫是否延迟发生; - 观察
MemStats.BySize中不同 size class 的Mallocs/Frees差值,判断对象是否被及时清扫(注意:Frees 不等于立即归还 OS)。
真正的“清除”动作发生在后台清扫器 goroutine 中,且受 runtime/debug.SetGCPercent(-1) 等参数抑制——这点极易被忽略:关闭 GC 不等于停止清扫,已分配的垃圾页仍驻留,直到显式调用 debug.FreeOSMemory() 或程序退出。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










