Go Runtime 是为 Go 程序提供基本服务的底层系统。它包括 goroutine 调度、内存管理、垃圾回收、堆栈管理以及其他运行时服务。本页面重点介绍这些核心运行时组件的实现细节,这些组件支持 Go 的并发模型、内存安全和高效执行。
有关 Go 编译器信息,请参阅 Go Compiler。
Go Runtime 主要在 src/runtime 包中实现,部分功能由汇编代码提供。与许多作为独立进程运行的语言运行时不同,Go Runtime 直接编译到每个 Go 程序中,使得每个 Go 二进制文件都是自包含的。
Runtime 承担着几项核心职责:
来源:src/runtime/proc.go22-115 src/runtime/runtime2.go16-108 src/runtime/mgc.go5-83
Go 调度器使用三部分模型(称为 G-P-M 模型)将 goroutine 多路复用到操作系统线程。
来源:src/runtime/proc.go22-115 src/runtime/runtime2.go395-507 src/runtime/runtime2.go527-622 src/runtime/runtime2.go636-757
三个主要组成部分是:
G (Goroutine):代表一个 goroutine,这是一个轻量级的执行线程。 g 结构包含:
stack、stackguard0、stackguard1)atomicstatus)- running、runnable、waiting 等。sched)- PC、SP 等。waiting)P (Processor):代表一个逻辑处理器(执行 Go 代码所需的资源)。 p 结构包含:
runq)mcache)status、schedtick)M (Machine):代表一个 OS 线程。 m 结构包含:
curg)p)tls)来源:src/runtime/runtime2.go395-507 src/runtime/runtime2.go527-622 src/runtime/runtime2.go636-757
调度器遵循以下关键原则:
调度器在 schedinit() 中初始化,并且主调度循环发生在 schedule() 中,该函数负责选择下一个要运行的 goroutine。
常见的调度器状态转换
| 操作 | 状态变更 | 描述 |
|---|---|---|
| 创建 | ⟶ _Grunnable | 创建新的 goroutine 并将其放入运行队列。 |
| 调度 | _Grunnable ⟶ _Grunning | 选择 goroutine 在线程上运行。 |
| 阻塞 | _Grunning ⟶ _Gwaiting | goroutine 被阻塞(例如,在通道或互斥锁上)。 |
| 解除阻塞 | _Gwaiting ⟶ _Grunnable | goroutine 被解除阻塞并放回运行队列。 |
| 系统调用 | _Grunning ⟶ _Gsyscall | goroutine 进入系统调用。 |
| 系统调用返回 | _Gsyscall ⟶ _Grunning | goroutine 从系统调用返回。 |
来源:src/runtime/proc.go820-921 src/runtime/runtime2.go16-108 src/runtime/proc.go1063-1083
Go 的内存管理系统由多个协同工作的组件组成,以高效地分配和释放内存。
来源:src/runtime/malloc.go5-101 src/runtime/mheap.go56-178 src/runtime/mcache.go src/runtime/mcentral.go
mheap:中央堆管理器,负责控制所有内存页。
mcentral:每个大小类的中央空闲列表。
mcache:每个 P 的空闲对象缓存。
mspan:代表一个连续的 page 块。
来源:src/runtime/malloc.go5-101 src/runtime/mheap.go56-178 src/runtime/mcache.go src/runtime/mcentral.go
分配路径遵循分层方法:
小对象分配(≤32KB)
大对象分配(>32KB)
微小分配(<16 字节,无指针)
来源:src/runtime/malloc.go5-101 src/runtime/malloc.go112-129
Go 使用并发的三色标记清除垃圾收集器。
来源: src/runtime/mgc.go5-83 src/runtime/mgcmark.go src/runtime/mgcsweep.go5-24
GC 周期包含以下主要阶段
清扫终止:
标记阶段:
标记终止:
清扫阶段:
来源: src/runtime/mgc.go5-83 src/runtime/mgcmark.go16-115 src/runtime/mgcsweep.go5-24
当堆增长达到阈值时触发 GC,该阈值由以下因素控制:
GOGC 环境变量(默认为 100%)——触发下一次 GC 的存活堆的百分比。GOMEMLIMIT 设置)runtime.GC() 强制 GCGC 使用调度算法来平衡
来源: src/runtime/mgc.go112-119 src/runtime/mgcpacer.go14-58
在标记阶段,运行时会启用写屏障来捕获可能导致对象遗漏的指针更新。写屏障
Go 使用分段栈,这些栈可以按需增长和收缩。
每个 goroutine 都有自己的栈,初始很小(例如 2KB),但可以增长到允许的最大值(64 位系统上为 1GB)。栈
来源: src/runtime/stack.go16-65 src/runtime/stack.go66-147
在垃圾回收期间,扫描栈以查找指向堆对象的引用
来源: src/runtime/mgcmark.go src/runtime/traceback.go16-37
运行时提供了 Go 并发原语的实现。
通道促进 goroutine 之间的安全通信
src/runtime/chan.gosudog 结构体来表示等待的 goroutine来源: src/runtime/chan.go src/runtime/proc.go319-370
计时器系统负责调度未来的事件
src/runtime/time.go来源: src/runtime/time.go46-117 src/runtime/time.go185-207 src/time/sleep.go
运行时实现了各种同步原语
这些原语依赖于原子操作和调度器来高效地管理 goroutine 的阻塞和唤醒。
来源: src/runtime/proc.go437-481 src/runtime/proc.go460-481
Go 运行时包含广泛的诊断功能
执行跟踪器捕获详细的运行时事件
跟踪器实现在 src/runtime/trace.go 中,可以通过 runtime/trace 包或使用 go tool trace 命令访问。
来源: src/runtime/trace.go5-18 src/runtime/trace.go28-140
运行时可以
runtime.Stack())来源: src/runtime/traceback.go16-76 src/runtime/export_test.go
当 Go 程序启动时,运行时会自行引导
runtime.schedinit() 中进行运行时初始化proc.go 中的 main 函数是入口点,它在调用用户的 main.main() 之前设置运行时。
来源: src/runtime/proc.go146-330 src/runtime/proc.go820-921
运行时通过以下方式与操作系统进行交互:
特定平台的实现保存在单独的文件中,例如 os_linux.go、os_windows.go 等。
来源: src/runtime/proc.go332-345 src/runtime/os_linux.go
Go 运行时是一个复杂的系统,为 Go 的并发模型、内存安全和性能提供了基础。理解其架构和组件有助于诊断问题、优化程序,并欣赏语言的设计决策。
有关特定组件的更多详细信息,请参阅相关的维基页面