原文地址:https://github.com/x1nchen/bl...
记录下这个问题是因为这不同于传统典型的 Go 服务内存泄漏。
8月15日前后,钉钉监控告警群不定时出现服务机器内存占用超 80%
告警,一开始以为是 Prometheus exporter 堆积造成的,准备下掉 Prometheus
exporter 的集成代码,后来觉 得这个堆积速度太快了点,不到一天就吃掉
6GB,十分夸张,所以有空的时候用 gops 快照了 一下,内存情况如下图
(pprof) /app/bin # gops memstats 1
alloc: 54.66MB (57317968 bytes)
total-alloc: 186.21GB (199938346560 bytes)
sys: 6.72GB (7215977904 bytes)
lookups: 0
mallocs: 2906600570
frees: 2906521966
heap-alloc: 54.66MB (57317968 bytes)
heap-sys: 6.44GB (6909984768 bytes)
heap-idle: 6.38GB (6846406656 bytes)
heap-in-use: 60.63MB (63578112 bytes)
heap-released: 6.33GB (6794371072 bytes)
heap-objects: 78604
stack-in-use: 2.12MB (2228224 bytes)
stack-sys: 2.12MB (2228224 bytes)
stack-mspan-inuse: 266.02KB (272408 bytes)
stack-mspan-sys: 31.88MB (33423360 bytes)
stack-mcache-inuse: 6.78KB (6944 bytes)
stack-mcache-sys: 16.00KB (16384 bytes)
other-sys: 4.58MB (4802459 bytes)
gc-sys: 251.55MB (263773240 bytes)
next-gc: when heap-alloc >= 100.19MB (105060944 bytes)
last-gc: 2020-08-14 20:54:01.063567774 +0800 CST
gc-pause-total: 982.706218ms
gc-pause: 17439
num-gc: 3484
enable-gc: true
debug-gc: false
分析从上面的指标可以得出以下结论
- 当前正在使用的堆内存大致是 60 多M
- HeapReleased = 6.33 GB 返还给操作系统的物理内存的字节数是 6.33GB (
HeapReleased 统计了从idle
span中返还给操作系统,没有被重新获取的内存大小)
那问题就抽象为:\为什么 HeapReleased 上升,RSS 没有下降?\
搜索了一下有人踩过这个坑了,参考:https://zhuanlan.zhihu.com/p/...
这是因为 Go 底层用 mmap 申请的内存,会用 madvise 释放内存。具体见
go/src/runtime/mem~linux~.go 的代码。madvise 将某段内存标记为不再使用时,有两种方式 MADV~DONTNEED~ 和
MADV~FREE~(通过标志参数传入):
- MADV~DONTNEED标记过的内存如果再次使用~,会触发缺页中断
- MADV~FREE标记过的内存~,内核会等到内存紧张时才会释放。在释放之前,这块内存依然
可以复用。这个特性从linux 4.5版本内核开始支持显然,MADV~FREE是一种用空间换时间的优化~。
- 在Go 1.12之前,linux 平台下 Go runtime
中的sysUnsed使用madvise(MADV~DONTNEED~)- 在Go 1.12之后,在MADV~FREE可用时会优先使用MADVFREE~
具体见 https://github.com/golang/go/...
Go
1.12之后,提供了一种方式强制回退使用MADV~DONTNEED的方式~,在执行程序前添加
GODEBUG=madvdontneed=1。具体见
https://github.com/golang/go/...
另外还有一个疑点:现在知道问题出现的条件是 Go \> 1.12 && Linux Kernel \>
4.5,但是 为什么之前阿里云没有出现,系统8月10号整体迁移到 aws 出现了?
调查发现
阿里云机器内核 4.4.0-105-generic,而 aws 机器内核
4.15.0-1054-aws,刚好符合预期,可以解释。
==
补充:8月24日
使用 GODEBUG=madvdontneed=1 强制回退使用
MADV~DONTNEED~,没有再出现内存泄漏问题。内存稳定在 200MB 左右
有疑问加站长微信联系(非本文作者)