
go 的垃圾回收由运行时(runtime)内置的原生代码实现,不依赖虚拟机;它采用并发标记清除算法,仅短暂 stw,与 java 的分代 gc 有本质区别。
Go 是一门编译型语言,源码直接编译为机器码,生成静态链接的原生二进制可执行文件——没有虚拟机层,也没有类似 JVM 的字节码解释器或 JIT 编译器。但这并不意味着 Go 缺乏运行时支持。恰恰相反,Go 自带一个高度集成、用 C 和汇编编写的 runtime 系统,它在程序启动时即初始化,全程管理内存分配、调度、栈管理及垃圾回收等核心任务。
垃圾回收(GC)正是由这个 runtime 中的原生(native)C/汇编代码模块完成的,而非 Go 语言本身编写的 goroutine。虽然 Go 程序中一切看似“由 goroutine 驱动”,但 GC 属于底层基础设施,必须在操作系统线程(OS thread)上高效、可控地运行。自 Go 1.1 起,GC 实现为并发(concurrent)标记清除(mark-and-sweep),并在 Go 1.5 后升级为三色标记 + 混合写屏障(hybrid write barrier) 的低延迟并发 GC。整个过程由 runtime 启动专用的后台 OS 线程(非用户可见的 goroutine)协作完成:
- 标记阶段大部分与用户代码并发执行;
- 清扫阶段亦可并发进行(Go 1.12+ 默认启用并发清扫);
- 全局暂停(Stop-The-World, STW)仅发生在标记开始前的根扫描(root scan)和标记终止(mark termination)两个极短环节,通常控制在 百微秒级(如 Go 1.22 中平均 STW
对比 Java GC:
- Java 采用分代回收模型(年轻代/老年代),配合多种算法(G1、ZGC、Shenandoah),侧重吞吐量或超低延迟,但需 JVM 统一管控内存生命周期;
- Go 采用统一堆(unified heap)+ 并发三色标记,无显式分代,设计目标是确定性低延迟与部署简洁性,避免调优负担。其 GC 触发主要基于堆增长比例(默认 GOGC=100,即堆增长 100% 时触发),而非固定时间或代阈值。
值得注意的是:
✅ GC 线程由 runtime 自动管理,开发者无需(也无法)手动启停或调度;
❌ 不应将 GC 线程误解为 goroutine——goroutine 是用户态调度单元,而 GC 运行在 OS 线程上,属于 runtime 内核行为;
⚠️ 可通过环境变量微调 GC 行为(如 GOGC=50 加密回收频率,GODEBUG=gctrace=1 输出 GC 日志),但生产环境推荐保持默认,依赖 runtime 的自适应策略。
示例:观察 GC 日志
GODEBUG=gctrace=1 ./myapp
输出类似:
gc 1 @0.012s 0%: 0.016+0.47+0.010 ms clock, 0.032+0.19/0.32/0.060+0.020 ms cpu, 4->4->2 MB, 5 MB goal, 4 P
其中 0.016+0.47+0.010 分别对应 STW mark setup / concurrent mark / STW mark termination 耗时。
总之,Go 的 GC 是一套轻量、内聚、面向云原生场景优化的自动内存管理系统——它隐身于 runtime 之中,由原生代码驱动,以极短暂停换取高响应性,真正做到了“看不见却始终可靠”。










