每日一Go-28、Go语言进阶-深入Go运行时:内存管理与GC
Go运行时(runtime)是Go高性能和高并发的核心支撑,其中内存管理与垃圾回收是关键。今天将深入底层机制,理解Go程序如何分配内存、如何决定数据的生命周期、以及Go垃圾回收器是如何工作的。 1、Go内存管理体系概览 Go使用一种分层式、针对并发优化的内存管理架构。可以概括为:线程本地分配(arena+mcache)+全局分配(central)+垃圾回收(GC),也就是减少锁争用+高速内存分配+自动回收。 2、内存分配器 Go的内存分配器模仿TCMalloc(TCMalloc—Design document for the C/C++ memory allocator TCMalloc, which the Go memory allocator is based on.),主要分三层: 2.1 堆(Heap):有runtime管理的大块连续内存区域 Go并不直接向操作系统请求小块内存,而是向操作系统申请一块(64MB大小)内存,这块内存在操作系统的术语叫Arena(竞技场),Go在Arena内做更细粒度的管理 2.2 中心缓存(Central Cache):全局共享的小对象池(加锁) Central按size class将对象划分为8B~32KB的多种规格: 每个size class都有一个central列表 用于分配中等频率的内存 有锁(mutex)保证多线程安全 2.3 mcache:每个P拥有的线程本地缓存(无锁) Go的GMP模型中,每个P(处理器)拥有一个mcache: 本地缓存,分配速度极快 小对象分配的时间复杂度是O(1),直接从mcache中拿 mcache没了才从Central Cache获取 3、小对象和大对象的分配策略 4、栈内存和堆内存 Go使用可憎长栈(由2KB~8GB): 栈内存快、无需GC,函数返回自动销毁 堆内存慢,需要GC管理 因此,尽可能把对象放在栈内存上,更高效,要做到这一点,依赖逃逸分析。 5、逃逸(Escape)分析 什么是逃逸?编译器决定变量放在栈上还是放在堆上,放在堆上的就产生了逃逸。可以用以下命令来查看自己的程序哪些地方产生了逃逸: 5.1 什么情况下变量会逃逸? 5.1.1 返回值逃出当前作用域 5.1.2 变量被存到interface、空interface时 5.1.3 闭包引用的外部变量 5.1.4 大量数据复制到channel时也可能逃逸 5.1.5 编译器无法证明变量的生命周期,例如发送指针到通道 6、Go垃圾回收(GC)整体流程 Go的GC是并发标记、并发清扫: 世界停止很短 GC是三色表记法 并发+增量+自适应(根据GOGC调整) 7、GC完整流程图 8、三色标记法(Tri-Color Marking) 三种颜色的含义: GC从Roots开始扫描(栈、全局变量、寄存器),步骤: 8.1 初始化:所有对象都是白色 8.2 将Root对象标记为灰色,进入灰色队列 8.3 循环处理灰色对象队列 对每个灰色对象:把它的子对象标记为灰色,自己变为黑色 8.4 结束时:黑色存活,白色将在Sweep阶段被回收 9、写屏障(Write Barrier):并发GC保证正确性的关键 并发GC时用户程序还在运行,会出现:新引用出现或者白对象被指向,导致对象遗漏,Go使用混合写屏障来保证三色不变性。 写屏障规则: 在程序写指针(p=q)时:将新引用的对象标记为灰色,将旧引用的对象标记为灰色(必要时) 核心保证:黑对象永远不指向白对象 10、Sweep阶段:并发清除白色对象,这个阶段和程序并发运行,不会产生STW(Stop the word) 11、GOGC:GC调度器 GOGC默认是100,表示:当本来GC后heap增长100%,再次触发GC。 12、从GC视角出发,如何写出更高效的代码? *源码地址* 1、公众号“Codee君”回复“每日一Go”获取源码 2、源码获取链接: https://pan.baidu.com/s/1B6pgLWfSgMngVeFfSTcPdg?pwd=jc1s 提取码: jc1s 如果您喜欢这篇文章,请点赞、推荐+分享给更多朋友,万分感谢!
文末有源码下载链接!
go build -gcflags "-m -m"




┌────────────┐
│ Root Scan │ ← STW(短暂)
└─────┬──────┘
│
▼
┌────────────┐
│ Marking │ ← 并发(goroutine 与 GC 同时运行)
├────────────┤
│ Write Barrier(写屏障) │
└─────┬──────┘
│
▼
┌────────────┐
│ Mark Done │ ← STW(短暂)
└─────┬──────┘
│
▼
┌────────────┐
│ Sweep │ ← 并发
└────────────┘
GOGC=50 // 更频繁 GC
GOGC=200 // 更少 GC
GOGC=off // 关闭自动 GC