在 Linux 上用 Go(Golang)写程序时,很多人以为“有 GC 就不会内存泄漏”,但实际上Go 也会内存泄漏,而且常见原因和 C/C++ 不太一样。下面从原理 + 常见原因 + 排查方法系统讲一下。
Go 有 GC,所以传统意义的“忘了 free”几乎不存在。
但 Go 的“内存泄漏”通常指:
本该被回收的对象,因为仍然被引用,导致一直占用内存,且不断增长
GC 只能回收不可达对象,只要还有引用,就不会回收。
var cache = map[string][]byte{}
func addCache(k string, v []byte) {
cache[k] = v
}
✅ 解决:
sync.Maplru 缓存库func worker() {
ch := make(chan int)
go func() {
for v := range ch {
_ = v
}
}()
// ch 没 close,goroutine 永远阻塞
}
pprof 里 goroutine 数量爆炸✅ 解决:
context.Context 控制生命周期ticker := time.NewTicker(time.Second)
go func() {
for range ticker.C {
// do something
}
}()
// 忘记 ticker.Stop()
✅ 解决:
defer ticker.Stop()
big := make([]byte, 10<<20)
small := big[:10]
// small 一直被引用
// 底层 10MB 数组无法释放
✅ 解决:
small := make([]byte, 10)
copy(small, big[:10])
func handler() func() {
big := make([]byte, 1<<20)
return func() {
_ = len(big)
}
}
C.malloc(1024)
// 忘记 C.free
top 看 RSS 一直涨✅ 解决:
C.freeresp, _ := http.Get(url)
// 忘记 resp.Body.Close()
正常情况:
RSS > 实际使用内存可用:
GODEBUG=madvdontneed=1
或:
debug.FreeOSMemory()
⚠️ 一般不建议强行调用
import _ "net/http/pprof"
go func() {
http.ListenAndServe(":6060", nil)
}()
查看:
go tool pprof http://localhost:6060/debug/pprof/heap
重点看:
inuse_spacegoroutinecurl http://localhost:6060/debug/pprof/goroutine?debug=1
持续增长 = 泄漏
top -p PID
cat /proc/PID/status
pmap -x PID
Linux 上 Go 内存泄漏,99% 不是 GC 的锅,而是“你还引用着”。
最常见三类:
如果你愿意,可以:
pprof 输出我可以直接帮你定位是哪一类泄漏。