Go进程RES内存远高于pprof/MemStats的原因排查求助
问题描述
我们有一个内部使用CGO库的Go进程,运行一段时间后出现OOM被系统杀死,内核日志如下:
kernel: [322533.632311] Memory cgroup out of memory: Killed process 1895550 (grpc_latest_srvc) total-vm:3072488kB, anon-rss:634580kB, file-rss:17192kB, shmem-rss:0kB, UID:0 pgtables:1812kB oom_score_adj:936
排查内存使用时,打印的runtime.MemStats和pprof堆文件显示堆内存仅60-100MB,但top命令的RES显示约620MB。虽已定时调用runtime.debug.FreeOSMemory()释放内存,但RES仍持续增长,最终导致OOM。请解释两者内存数据差异的原因。
当前runtime.MemStats数据:
Alloc: 104 MiB TotalAlloc: 366909 MiB StackInuse: 1152 KiB Sys: 155 MiB NumGC: 84458
top命令结果:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ 69917 root 20 0 3072488 620116 31160 S 141.8 3.8 88:59.63
内存数据差异原因分析
- CGO分配的内存不受Go Runtime追踪:Go的
runtime.MemStats和pprof堆分析只负责统计Go自身内存管理器分配的内存,而CGO调用C代码时用malloc等C标准库分配的内存,完全脱离Go的管控,不会被计入MemStats的任何字段,也不会出现在Go的pprof堆文件里。这是RES远大于Go堆内存的核心原因。 - C侧内存泄漏:如果CGO依赖的C库存在内存泄漏——比如分配内存后没调用
free释放,这部分内存会一直占用系统物理内存,Go既感知不到也没法回收,自然会导致RES持续增长,最终触发OOM。 runtime.debug.FreeOSMemory()对CGO内存无效:这个函数只能让Go Runtime把自身闲置的堆内存还给操作系统,对CGO分配的C内存完全起不到作用,所以调用它没法降低RES。- 其他未统计的内存项:比如进程的C线程栈(不是Go的goroutine栈)、共享内存的私有部分、内核页表等,这些也会被计入RES,但结合你的场景,CGO内存是最主要的影响因素。
内容的提问来源于stack exchange,提问作者samba
相关产品推荐
相关产品推荐

