会,但不是 context 树节点本身泄漏,而是它拖住的 timer、channel 和闭包变量长期驻留堆上;pprof -heap 显示大量 *time.timer、chan struct{} 或 []uint8 实例数随请求线性增长即为铁证,因不调用 cancel 导致 ctx.done() 永不关闭,监听它的 goroutine 卡在 select { case

context.WithCancel 返回的 cancel 函数不调用,真会泄漏内存吗?
会,但不是 context 树节点本身泄漏,而是它拖住的 timer、channel 和闭包变量长期驻留堆上。pprof -heap 里看到大量 *time.Timer、chan struct{} 或 []uint8 实例数随请求线性增长,基本就是 cancel 没调用的铁证。
- cancel 函数本质是向内部 channel 发一次 close;不调用 →
ctx.Done()永远不关闭 → 所有监听它的 goroutine 卡在select { case 里不动 - 父 context(比如
*timerCtx)会把子 context 记进children map[*timerCtx]struct{},cancel 不调 → 父强引用子 → 子及其闭包捕获的所有变量(哪怕只是个*bytes.Buffer)全被钉住 - HTTP handler 中常见写法:
ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second),但 handler 提前 return 了,defer cancel()没执行 → 泄漏立刻发生
为什么 defer cancel() 在无限循环里完全失效?
defer 只在函数 return 时触发,不是“goroutine 结束时”或“程序退出时”。只要函数没返回,defer 就压着不执行,cancel 函数永远不跑。
- 典型反例:
for { select { case ,watch channel 关闭后持续读出零值,进入死循环 → 函数永不返回 → <code>defer cancel()彻底失能 - etcd watch 场景中,
wchan := eClient.Watch(ctx, ...)返回的 channel 被 cancel 后关闭,但仍可不断读到零值,循环停不下来 - 解决办法不是加更多 defer,而是把 cancel 提前到循环外显式调用,或确保循环有明确退出路径(如检查
resp.Err() != nil)
context.Value 存大对象到底有多危险?
存的是指针,就等于把整个对象生命周期绑定到 context 生命周期上。根 context(如 context.Background())活多久,那个对象就锁内存多久。
Colly 是一个用于 Go 语言的快速开源爬取和爬虫框架。它适用于从简单的页面提取到异步爬虫处理大量页面集合,支持请求回调和结构化解析。
- 常见误用:
ctx = context.WithValue(ctx, key, &hugeStruct{})—— 整个结构体被强引用,GC 无法回收 - pprof -inuse_space 显示高位
*bytes.Buffer或[][]byte,调用栈指向 middleware 的ctx.WithValue,但你根本没 new 这些 buffer,根源就是 Value 里塞了不该塞的东西 - 只存轻量标识:用户 ID、trace ID、request ID —— 字符串或整数;别存结构体指针,也别反复嵌套
WithValue构建新 context
怎么确认自己有没有漏掉 cancel 调用?
静态分析难全覆盖,得靠运行时观测 + 代码约定双管齐下。
- 所有
http.HandlerFunc、grpc.UnaryServerInterceptor入口处,强制写defer cancel(),除非有明确延迟取消逻辑(比如启动后台任务并返回 cancel 句柄) - 用
go tool trace观察长期存活的 goroutine,筛选状态为runtime.gopark且等待chan receive的实例,再反查其 ctx 来源 - 测试时可用 wrapper 记录 cancel 调用状态:
trackCancel函数包装WithCancel,在 test 中断言called == true
真正麻烦的从来不是“不知道怎么写”,而是“写了却没生效”——比如 defer 被死循环拦住,或者 cancel 被传进结构体字段后彻底失联。这些地方没有报错,只有内存和 goroutine 数悄悄涨。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










