为什么Golang MADV_FREE有时会导致OOM?

zazmityj  于 2022-12-07  发布在  Go
关注(0)|答案(1)|浏览(296)

我们使用go1.12和k8s部署服务,在实际生产环境中,我们有一个项目一直OOM到容器被杀,通过网上调查,是因为Golang MADV_FREE,后来我们设置为MADV_DONTNEED,问题解决了。
网上说MADV_Free是指系统只有在感觉到压力的时候才释放内存,但是内存分配一直都在发生,我们的其他服务都在同样的环境中,为什么没有OOM发生呢?

46qrfjad

46qrfjad1#

嗯,我怀疑这样的问题是否适合,因为它不太可能有简短的答案,不过,让我试试看。
首先要考虑的是,当内核发现内存不足时,内核中的OOM killer只会找到内存消耗最高的进程¹并将其关闭。(我希望您说的是内核中的OOM killer,而不是一些k8特定的服务或您内部开发的东西。)
然后,让我们考虑已经切换到使用MADV_FREEGo 1.12 release notes
在Linux上,运行时现在使用MADV_FREE来释放未使用的内存。这更有效率,但 * 可能会导致报告的RSS更高。* 内核将在需要时回收未使用的数据。要恢复到Go 1.11的行为(MADV_DONTNEED),请设置环境变量GODEBUG=madvdontneed=1
(强调我的。)
这意味着,如果一个程序是用Go语言1.12编译的,在标准负载下运行了一段时间,然后同一个程序在相同的负载下运行了相同的时间,但是使用了GODEBUG=madvdontneed=1设置,那么在第一种情况下,RSS的 * 表观 * 消耗--* 从外部看 * --将高于第二种情况。
重申一下,Go语言内存管理器实际标记为madvise(2)的内存页的数量在两次运行中大致相同,但是由于两种方法释放的内存页的处理语义不同,使用的RSS的读数会有所不同,不是实际的内存使用情况,而是RSS的读数。
这显然使得使用MADV_FREE将内存返回给操作系统的进程更有可能被OOM杀手选中。
话虽如此,我还是建议你从一个不同的Angular 来看待这个问题。测量一个用Go语言编写的程序的内存消耗并不是毫无用处的,它只对捕捉一些“明显”的东西有用,比如在多个GC扫描周期内内存的稳定增长,这可能意味着内存泄漏。要真正评估真实的使用的模式,你必须使用正在运行的程序的Go运行时提供的mertics。我已经尝试详细说明了使用here的原因。
所以我想说,您最好集中精力修复OOM killer设置或类似的东西(注意,有可能使某个特定的进程不受OOM killer的影响)。
还要注意的是,你使用的是一个非常旧的不受支持的Go语言版本,而在Go语言1.16中,madvisehas been reverted的行为又一次出现了,所以它又一次使用了MADV_DONTNEED
¹实际上比这更复杂,因为OOM killer有一组启发式方法,它使用这些方法来查找“资源占用”,而内存消耗只是它考虑的指标之一。

相关问题