我们的一项 Go 服务中的 RSS 不断增加。
我们怀疑这是由于 scavenger 没有正确地将内存返回给操作系统(或者操作系统由于使用 MADV_FREE 而没有收回内存)。通过 pprof 检查,未检测到内存泄漏。
我们尝试使用以下简单的 Go 程序进行一些实验:
Go版本:go1.13.4 linux/amd64(也尝试过go1.13.1)
package main
import (
"fmt"
"time"
)
func allocate(s int) {
a := make([]byte, s * 1024 * 1024)
for i := 0;i < len(a); i += 4096 {
a[i] = 'x'
}
fmt.Printf("%c %d\n", a[0], s)
}
func main() {
allocate(100)
allocate(200)
allocate(500)
time.Sleep(300 * time.Second)
}
我们与
strace -q -e trace=memory go run main.go
并且结果并不一致:
-
https://pastebin.com/sGw3dp9E https://pastebin.com/sGw3dp9E(没有报告 MADV_FREE)
-
https://pastebin.com/d6CmRfD4 https://pastebin.com/d6CmRfD4(报告了MADV_FREE,但为什么释放的值这么小?)
使用 gctrace 运行,我们得到:https://pastebin.com/6JaC2r85 https://pastebin.com/6JaC2r85
我们通过 top 和 pmap 确认 RSS 仍然很高:
$ pmap -x 22712
22712: /tmp/go-build073210395/b001/exe/main
Address Kbytes RSS Dirty Mode Mapping
0000000000400000 576 428 0 r-x-- main
0000000000490000 756 320 0 r---- main
000000000054d000 84 56 32 rw--- main
0000000000562000 120 36 36 rw--- [ anon ]
000000c000000000 983040 819440 397680 rw--- [ anon ]
00007fcdfb2e1000 66684 26780 26780 rw--- [ anon ]
00007fffa3ba2000 132 20 20 rw--- [ stack ]
00007fffa3bdf000 8 4 0 r-x-- [ anon ]
ffffffffff600000 4 0 0 r-x-- [ anon ]
---------------- ------- ------- -------
total kB 1051404 847084 424548
如果有人可以帮助澄清以下问题,我们将不胜感激:
- 根据https://github.com/golang/go/issues/30333 https://github.com/golang/go/issues/30333,在 go 1.13 中,应该定期执行清理,而不是等待 5 分钟的间隔,但 strace 有时没有记录 MADV_FREE。 gctrace 确实打印有关清理的日志,但似乎它并没有真正调用 MADV_FREE?我是否遗漏了有关 go 1.13 中的清理逻辑的一些内容?
- 尝试使用 GODEBUG=madvdontneed=1,结果更好,但 RSS 仍然徘徊在 500MB 左右,只有当我们将 madvdontneed 与 debug.FreeOSMemory() 结合时,我们才得到 RSS
附加说明:在 Windows 上使用 go 1.13 运行相同的程序似乎达到了预期的效果,即内存逐渐释放回操作系统。