Files
cs-note/hzh/GolangStar/Go面试题库/垃圾回收面试题.md
T

8.6 KiB
Raw Blame History

tags, create time
tags create time
go
golang
interview
gc-questions
2026-06-07 14:30

垃圾回收面试题 ♻️

概述

本文件涵盖 Go GC(Garbage Collection)的 14 道高频面试题,涉及 GC 算法、三色标记、写屏障、STW、调优等核心概念。GC 是 Go 运行时的重要组成部分,理解它对于编写高性能程序至关重要。

关联笔记

正文

Q1:常见的 GC 实现方式有哪些? 🟡中等

参考答案

算法 特点
标记清扫 从根对象出发标记存活对象,清扫不可达对象
标记整理 在标记过程中整理对象到连续内存,解决碎片
增量式 将标记和清扫分批执行,降低停顿时间
分代式 按存活时间分年轻代/老年代/永久代
引用计数 引用归零时立即回收,但有循环引用问题

[!note] 📝 核心考点 Go 采用的是:无分代 + 不整理 + 并发 + 三色标记清扫。面试时可以对比 Java 的分代 GC 来说明 Go 的设计取舍。


Q2:Go 的 GC 使用的是什么算法? 🟢简单

参考答案

无分代、不整理、并发的三色标记清扫算法。

[!tip] 💡 面试技巧 三个关键词要记住:"无分代"(不做年龄分类)、"不整理"(不移动对象)、"并发"(和用户代码同时运行)。


Q3:三色标记法是什么? 🟡中等

[!question] ❓ 思考一下 如果 GC 扫描和程序运行同时进行,可能会出现什么情况导致错误回收对象?

参考答案

三色标记通过三种颜色区分对象状态:

颜色 含义 最终命运
白色 未被访问 被回收(如果是垃圾)
灰色 已被访问但未扫描完子节点 待处理队列
黑色 已被访问且所有子节点已扫描完 确认存活

标记流程:

graph LR
    A[全部白色] --> B[根对象标为灰色]
    B --> C{灰色队列空?}
    C -->|否| D[取出灰色对象]
    D --> E[扫描其引用的对象]
    E --> F{引用是白色?}
    F -->|是| G[标为灰色]
    F -->|否| H[不变色]
    E --> I[当前对象标为黑色]
    I --> C
    C -->|是| J[标记完成: 黑色存活, 白色回收]
    
    style A fill:#FFFFFF
    style B fill:#FF9800,color:#fff
    style I fill:#4CAF50,color:#fff
    style J fill:#F44336,color:#fff

[!info] 🔗 延伸阅读


Q4:Go 语言 GC 的根对象是什么? 🟢简单

参考答案

根对象(Root Set)包括三类:

类型 说明
全局变量 编译期确定的、贯穿程序生命周期的变量
执行栈 每个 goroutine 的栈上变量及指向堆的指针
寄存器 可能表示堆内存指针的寄存器值

Q5:STW 是什么意思? 🟢简单

参考答案

STW = Stop the World,指用户代码被完全停止运行的阶段。STW 越长,对延迟的影响越大。

早期 Go 的 STW 长达几百毫秒,现代 Go 已优化到微秒级。

[!warning] ⚠️ 高频陷阱 虽然大部分 GC 工作是并发的,但仍有两个 STW 阶段:MarkTermination(标记终止)和 SweepTermination(清扫终止),这两个阶段的 STW 时间需要严格控制。


Q6:并发标记清除法的难点? 🟡中等

[!question] ❓ 思考一下 如果 GC 在标记黑色对象 C 的子节点时,用户程序并发地将 C 的引用从白色对象 B 改到了另一个白色对象 D,会发生什么?

参考答案

核心难点:如何在用户程序并发修改引用时保证 GC 正确性。

两大问题:

问题 描述
对象消失 黑色对象到白色对象的引用被删除,同时灰色到白色的引用也被删除,白色对象被错误回收
新对象处理 标记期间新分配的对象如何着色?标白可能误回收,标黑可能造成浮动垃圾

[!warning] ⚠️ 高频陷阱 "对象消失"是并发 GC 最经典的问题。黑色对象不再被重新扫描,如果它与白色对象之间的唯一路径被切断,这个白色对象就会被误回收。

[!info] 🔗 延伸阅读


Q7:Go 如何解决并发标记时的引用修改问题? 🟡中等

参考答案

通过写屏障技术和三色不变性维护:

混合写屏障(Go 1.8+):

操作 处理方式
新建引用 将目标对象着为灰色
删除引用 将被删对象标为灰色
栈上新建对象 默认标记为黑色(无需重扫)

弱三色不变性:允许黑色对象指向白色对象,但要保证从白色对象出发存在全灰色路径可达根对象。

[!tip] 💡 面试技巧 "写屏障就像在每次指针赋值时安了一个监控摄像头——只要有人改了引用关系,就立刻把相关对象重新检查一遍,确保不会漏掉任何可达对象。"


Q8:什么是写屏障、混合写屏障? 🟡中等

参考答案

写屏障是在编译器指针赋值操作中插入的额外短指令:

类型 时机 处理方式 局限
插入写屏障 (Dijkstra) 建立新引用时 目标标灰 删除引用时无保护
删除写屏障 (Yuasa) 删除引用时 原对象标灰 新建引用时无保护
混合写屏障 (Go 1.8+) 两者都处理 建立→目标标灰;删除→原对象标灰 需特殊处理栈操作

[!note] 📝 核心考点 混合写屏障的关键优化:标记阶段创建的栈上新对象默认标记为黑色,因此不需要 STW 重扫栈——因为栈本身就是黑色的,它指向的对象必然是可达的。


Q9:Go GC 的流程? 🟡中等

参考答案

阶段 说明 状态
SweepTermination 清扫终止,为下一轮做准备 STW
Mark 扫描标记,与用户代码并发 并发
MarkTermination 标记终止,保证周期内完成 STW
GC off 内存清扫,归还可回收内存 并发
GC off 内存归还,释放多余内存给 OS 并发

Q10:GC 触发的时机? 🟢简单

参考答案

触发方式 条件
主动触发 runtime.GC() 阻塞式等待完成
被动触发 ① 超过 2 分钟没有 GC → 强制触发
② 内存增长达到阈值(GOGC,默认 100%)

第一次 GC 的触发临界值是 4MB。

[!tip] 💡 面试技巧 "GOGC=100 意味着当当前堆大小超过上次 GC 后标记堆大小的 100% 时触发。可以通过 debug.SetGCPercent(500) 调整。"


Q11:GC 关注的指标? 🟢简单

参考答案

指标 说明
CPU 利用率 GC 占用多少 CPU
GC 停顿时间 STW 和 Mark Assist 造成的停顿
GC 停顿频率 多久触发一次 GC
GC 可扩展性 堆变大时性能如何衰减

Q12:有了 GC 为什么还会发生内存泄漏? 🟢简单

参考答案

GC 只能回收不可达的对象。如果对象仍然被根对象引用但业务上已不再使用,就会泄漏:

  1. 被根对象持有:局部变量被赋值到全局 map 中
  2. goroutine 泄漏:goroutine 不能正常退出,持有的所有变量都无法回收

Q13:Go 的 GC 如何调优? 🟡中等

参考答案

手段 说明
减少内存分配 使用 sync.Pool 复用对象
调整 GOGC 增大 GOGC 值减少 GC 频率
提高分配速度 合理化内存分配,提高 CPU 利用率

Q14:如何观察 Go GC? 🟡中等

参考答案

四种方式:

方式 适用场景
GODEBUG=gctrace=1 输出详细的 GC 日志
go tool trace 可视化展示 GC 和执行信息
debug.ReadGCStats 代码内监控 GC 状态
runtime.ReadMemStats 监控内存相关指标

[!info] 🔗 延伸阅读

关联笔记