Skip to content

内存与垃圾回收

逃逸分析、三色标记、混合写屏障、GOGC 调优。

Updated View as Markdown
For humans

内存与垃圾回收

Go 的 GC 设计目标:低延迟优先(STW 毫秒级),换吞吐。面试主线:对象去哪、GC 怎么标记、怎么调。

逃逸分析

func f() *int {
    x := 1
    return &x      // x 逃逸: 分配在堆上, 函数返回后仍存活
}
  • 逃逸分析:编译器判断变量能否留在栈上;取地址返回、传入接口、闭包捕获都会导致逃逸
  • 栈上分配:零 GC 开销(随函数退出自动释放);堆上分配:进 GC 管
  • 优化:减少逃逸(避免返回指针、避免不必要接口装箱)是 Go 性能调优方向

三色标记

三色标记: 灰是工作队列, 黑是存活, 白是垃圾

  • 黑色:已扫描完(存活),灰色:已发现、引用未扫描(工作队列),白色:未发现(候选垃圾)
  • 从根(全局、栈、寄存器)出发,灰变黑、引用变灰,直到无灰 → 白色回收
  • 并发标记:GC 和业务 goroutine 同时跑,需要写屏障保证不误回收(见下)

混合写屏障

并发 GC 的核心难题:标记期间业务代码改了引用怎么办(黑对象引用了白对象,白对象会被误回收)。

写屏障:对象引用被修改时,拦截并标记新引用(插入屏障)或旧引用(删除屏障)。

Go 1.8+ 用混合写屏障(插入 + 删除):

  • 标记和业务并发执行,不需要 STW 全停
  • 结合三色不变式:黑色对象不能直接引用白色对象(屏障保证)
  • 收益:STW 压缩到毫秒级(标记阶段基本不暂停)

GOGC 调优

参数 含义
GOGC(默认 100) 堆增长 100% 触发 GC:上一轮活 10MB,堆到 20MB 触发
GOMEMLIMIT(1.19+) 内存软上限,减少 GC 频率
debug.SetGCPercent 运行时调整
  • GOGC 越大 GC 越少(省 CPU 但内存峰值高),越小越频繁(内存稳但 CPU 换)
  • 调优方向:看 pprof 的 heap profile 和 GC 统计(runtime/metrics
  • 面试话术:低延迟场景调大 GOGC(少 GC),内存受限场景调小;1.19 后用 GOMEMLIMIT 更平滑

面试追问

  1. 逃逸分析是什么? 编译器判断变量分配栈还是堆。返回指针/接口装箱/闭包捕获会逃逸
  2. 三色标记? 白(候选垃圾)灰(待扫描)黑(存活)。从根出发标记,无灰后清白
  3. 写屏障解决什么? 并发标记时引用被改,防误回收。Go 用混合写屏障,STW 毫秒级
  4. GOGC 怎么调? 堆增长阈值(默认 100%)。大 → 少 GC 高内存,小 → 反之。GOMEMLIMIT 更平滑
  5. Go GC 的目标? 低延迟优先:STW 毫秒级,牺牲部分吞吐。和 Java G1 思路类似
Navigation

Type to search…

↑↓ navigate↵ selectEsc close