Image 1

本文永久链接https://tonybai.com/2025/07/24/deadlock-detection-by-gc

大家好,我是Tony Bai。

Go 语言的 go 关键字让并发编程变得前所未有的简单,但也带来了新的挑战。当所有 goroutine 都陷入阻塞时,Go runtime 会报告一个“全局死锁”并终止程序。然而,更常见也更隐蔽的是部分死锁:一部分 goroutine 永久阻塞,而程序的其他部分仍在运行。

Image 2

图: Uber生产服务中因部分死锁导致的goroutine数量变化 如上图所示,这些泄漏的 goroutine 会像“僵尸”一样持续占用内存和资源,在长周期运行的服务中导致内存泄漏、CPU 升高,甚至系统崩溃(Uber工作日的重新部署掩盖了泄漏,但在周末和节假日期间,数字会激增)。现有的工具如 goleak 主要用于测试环境,难以在生产中大规模部署。

这些难以追踪的“部分死锁”在长周期服务中如同定时炸弹。现在,一项革命性的Go提案(#74609)带来了希望:通过赋予垃圾收集器(GC)“新技能”,使其能够直接在运行时检测出这些永久阻塞的 goroutine。这个想法不只是停留在理论层面,其原型工具 GOLF 已经在 Uber 的生产环境中成功验证,发现了数百个此前未被察觉的死锁。本文将和大家一起解读一下这一前沿技术,揭示 Go GC 是如何被改造为并发问题“侦探”的。

核心思想:当“内存不可达”遇上“并发不可达”

这项新提案的核心洞见,是将垃圾收集中的内存可达性与并发编程中的**活跃性(liveness)**巧妙地联系起来。

我们知道,一个被阻塞的 goroutine(例如,等待从一个 channel 接收数据 <-ch)能否被唤醒,取决于是否有另一个“活跃”的 goroutine 能够对同一个并发原语(这里的 ch)执行配对操作(例如 ch <- data)。

提案的关键假设是:

如果一个被阻塞的 goroutine,其所等待的所有并发原语(channel、mutex 等),从所有当前可运行(runnable)的 goroutine 的视角来看,在内存中都是不可达的,那么这个被阻塞的 goroutine 永远不可能被唤醒——它已经陷入了部分死锁。

换句话说,如果没有任何一个“活人”能找到唤醒你所需的“钥匙”,那你就是一个“僵尸”。

而判断“内存可达性”,正是 Go GC 的核心工作。

GOLF:一个扩展版的 Go 垃圾收集器

研究人员将此思想实现为一个名为 GOLF (Goroutine Leak Fixer) 的工具,它对 Go 的标准 mark-and-sweep GC 进行了扩展。

Image 3

图: 对GC周期的扩展 GOLF 的工作流程大致如下:

  • 修改 GC Root Set:在 GC 的标记(Marking)阶段开始时,GOLF 不再像标准 GC 那样将所有 goroutine 视为根对象(GC Roots)。相反,它只将当前处于**可运行状态(runnable)**的 goroutine 作为初始的根集合。

  • 迭代标记与扩展

    • a. GC 从这个最小化的根集合出发,进行第一轮内存可达性标记。
    • b. 标记完成后,GOLF 会检查所有仍处于阻塞状态的 goroutine。
    • c. 对于每个阻塞的 goroutine,它会检查其等待的并发原语(如 channel)是否在刚刚的标记过程中被标记为“可达”。
    • d. 如果一个阻塞 goroutine 等待的某个原语是“可达”的,那么这个 goroutine 就有可能被唤醒。GOLF 称其为**“可达活跃”(reachably live),并将其加入到 GC 的根集合中**。
    • e. 重复 a-d 步骤,直到在一个完整的迭代中,根集合不再扩大。
  • 死锁判定:当迭代稳定后,所有未被加入根集合的、仍处于阻塞状态的 goroutine,都被判定为部分死锁

提案中的实现细节

Go 官方 issue #74609 中讨论的实现,是基于上述学术研究的简化和工程化版本:

  • API 触发:为了将性能影响降到最低,这种增强的 GC 周期不会默认开启,而是通过一个新的 API 来手动触发。
  • 不强制回收:与学术论文中可以强制回收泄漏 goroutine 内存的“Recovery”功能不同,提案的初步实现仅将检测到的 goroutine 标记为死锁,并将其视为永久可达,以避免破坏 Go 的内存安全语义(例如,意外触发 finalizer)。
  • 实验性标志:该功能将通过 GOEXPERIMENT=deadlockgc 标志启用,表明其仍处于实验阶段。

惊人的实验结果:在 Uber 生产环境中大显身手

这项研究的有效性在多个层面得到了验证:

  • 微基准测试:在包含 121 个已知可能导致死锁的 go 语句的微基准测试中,GOLF 成功检测出了 94.75% 的部分死锁。

  • 大型代码库:在 Uber 的一个包含 180 万行 Go 代码的子集上运行时,GOLF 发现了 357 个已知泄漏中的 180 个(约 50%)。

  • 生产环境部署:GOLF 被部署到一个真实的 Uber 生产服务中,在 24 小时内,成功检测到了由 3 个不同编程错误导致的 252 个部分死锁实例。这些问题是之前通过测试未能发现的。

更重要的是,性能测试表明,即使在最坏的情况下,GOLF 带来的 GC 标记阶段的 slowdown 仍然在可接受的范围内,而对于存在大量泄漏的程序,它甚至可能因为减少了需要标记的内存而加速 GC

对 Go 开发者的意义

这项提案一旦被采纳并最终进入 Go 的稳定版本,将对 Go 并发编程生态产生深远影响:

  1. 新一代调试利器:开发者将获得一个强大的、内建于运行时的工具,用于诊断最棘手的并发问题,尤其是在复杂的、长周期运行的微服务中。
  2. 提升生产环境的稳定性:通过在生产中按需触发死锁检测,运维团队可以主动发现并定位潜在的内存泄漏源头,防止其演变为严重的线上事故。
  3. 补充现有工具的盲区:GOLF 的动态、在线检测能力,与 goleak 等基于测试的离线检测工具形成了完美的互补。

小结:从生产验证到 Go 1.26 的未来

将死锁检测的逻辑与垃圾收集的机制相结合,是一次天才般的跨界创新。它利用了 GC 对程序内存图谱的全局视野,以一种理论上可靠且实践中高效的方式,为解决 Go 并发编程中的“部分死锁”顽疾提供了全新的思路。事实上,Go 核心开发者 Rick Hudson 早在十年前就曾勾勒出类似的方法。

而这次,它不再仅仅是一个构想。 Uber 在生产环境中的成功部署和验证,为这项技术的可行性和实用价值提供了强有力的证明。这正是推动该提案在 Go 官方层面迅速获得关注的关键。

在最近的 Go 编译器与运行时会议上,这项来自 Uber 的提案再次成为焦点。Go 团队的核心成员 Michael Knyszek 确认,他们已经收到了 Uber 提交的补丁,并高度评价了其在生产环境中提供的“有用数据”。尽管该方法存在一些漏报(false negatives),但其**不会误报(false positives)**的特性使其极具实用价值。

会议讨论进一步明确了该功能的未来方向:

  • 明确的目标版本:团队计划推动这项提案在 Go 1.26 开发周期中落地,以避免其在周期后期才被仓促合入。
  • API 形式:最有可能的 API 形式是将其作为一个新的 pprof profile 类型暴露出来。这意味着开发者未来或许可以通过 http://…/debug/pprof/goroutineleak 或类似的端点来按需触发检测。
  • 集成场景
    • 在测试中:可以与 testing 包集成,但必须是**可选加入(opt-in)**的,因为许多现有测试可能无意中存在 goroutine 泄漏。
    • 在生产中:它将无缝集成到**持续性能分析(continuous profiling)**系统中,成为诊断线上问题的强大武器。

值得注意的是,Go 团队强调,这个功能的目标是检测和报告泄漏,而不是自动回收。“泄漏的 goroutine 是 bug”,团队明确表示不会冒险去运行这些卡死 goroutine 的 finalizer,因为这可能导致不可预测的行为。

虽然该实现目前尚未移植到最新的 Green Tea GC,并且在 32 位系统上支持有限,但其方向已经非常明确。一个酝酿了十年的构想,在学术界和工业界(Uber)的共同推动下,正以前所未有的速度接近现实。我们有理由期待,在 Go 1.26 中,Go 开发者将迎来一个内建于运行时的、经过生产环境检验的革命性并发问题诊断工具。

资料链接:


你的Go技能,是否也卡在了“熟练”到“精通”的瓶颈期?

  • 想写出更地道、更健壮的Go代码,却总在细节上踩坑?
  • 渴望提升软件设计能力,驾驭复杂Go项目却缺乏章法?
  • 想打造生产级的Go服务,却在工程化实践中屡屡受挫?

继《Go语言第一课》后,我的《Go语言进阶课》终于在极客时间与大家见面了!

我的全新极客时间专栏 《Tony Bai·Go语言进阶课》就是为这样的你量身打造!30+讲硬核内容,带你夯实语法认知,提升设计思维,锻造工程实践能力,更有实战项目串讲。

目标只有一个:助你完成从“Go熟练工”到“Go专家”的蜕变! 现在就加入,让你的Go技能再上一个新台阶!

Image 4


商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

Image 5