go 的垃圾回收(gc)由 go 运行时(runtime)提供的原生 c/汇编代码实现,而非虚拟机或 go 语言自身;它采用并发标记清除算法,仅短暂 stw(stop-the-world),与 java jvm 的分代 gc 有本质区别。
Go 编译生成的是静态链接的原生二进制文件,不依赖虚拟机(如 JVM),但这并不意味着它缺少运行时基础设施。相反,Go 自带一个高度集成的用户态运行时系统(Go runtime),它以静态库形式嵌入每个 Go 程序中,负责调度、内存管理、网络 I/O、栈管理及——最关键之一——垃圾回收。
尽管 Go 程序通过 goroutine 抽象并发,但 GC 的核心逻辑完全运行在 runtime 层,用 C 和汇编编写(见 $GOROOT/src/runtime/mgc*.go 及 mgc.c),由 Go 启动时初始化的后台 OS 线程(OS threads) 执行。这些线程独立于 GMP 调度模型中的 M(machine),但共享同一套线程池资源。例如:
✅ 正确理解:goroutine 是用户代码的并发抽象;而 GC 是 runtime 的系统级服务,它使用 OS 线程(非 goroutine)完成“脏活”,且对 Go 程序员完全透明。
Go 的 GC 并非全程 Stop-the-World。它仅在两个关键瞬间触发短暂 STW(通常

其余时间(如并发标记、并发清扫、辅助标记),用户 goroutine 持续运行。这与传统 JVM 的 Full GC(可能暂停数百毫秒)形成鲜明对比。
| 维度 | Go GC(自 v1.5 起) | Java HotSpot GC(G1/ZGC 为例) |
|---|---|---|
| 模型 | 统一堆 + 并发三色标记-清除 | 分代(Young/Old)+ 多种收集器可选 |
| 暂停目标 | 面向低延迟( | 面向吞吐或延迟平衡(ZGC 目标 |
| 内存管理 | 基于 span/page 的紧凑分配器 + mcache/mcentral/mheap | Card Table / Remembered Set / Region |
| 触发时机 | 基于堆增长率(GOGC=100 默认:当新分配量达上次 GC 后存活堆的 100% 时触发) | 基于阈值、时间预测、GC 日志策略等 |
你可以通过以下方式观察 GC 行为:
# 启用 GC 调试日志(开发/调试用)
GODEBUG=gctrace=1 ./your-program
# 查看实时 GC 统计(程序内)
import "runtime"
var memStats runtime.MemStats
runtime.ReadMemStats(&memStats)
fmt.Printf("Next GC: %v MB, NumGC: %d\n",
memStats.NextGC/1024/1024, memStats.NumGC)⚠️ 注意事项:
总之,Go 的 GC 是一套深度定制、紧贴硬件、以低延迟为第一目标的原生运行时组件——它不靠 VM,而靠精巧的 C/汇编实现与 OS 线程协同,是 Go “简洁即强大”哲学的典型体现。