美文网首页
【golang】特定版本Golang应用内存泄露问题处理

【golang】特定版本Golang应用内存泄露问题处理

作者: Bogon | 来源:发表于2022-05-17 00:38 被阅读0次

    线上一个服务,启动后 RSS 随任务数增加而持续上升,但是过了业务高峰期后,任务数已经下降,RSS 却没有下降,而是维持在高位水平。那内存到底被谁持有了呢?

    内存使用量(mem.rss)居高不下,且低峰期未下降,怀疑发生了内存泄漏现象。

    排查

    刚开始怀疑时内存泄漏,但是抓取 pprof heap 图观察后,未发现泄露问题,且内存分配符合预期;

    发现内存使用虽然居高不下,但未呈上涨趋势,因此修改关键字为“go 内存占用居高不下”,发现有相同问题;

    结论

    好多gopher都遇到过的这个问题,如果你的Go服务版本是大于等于1.12并且小于1.16的话,大概率也会遇到这个问题。

    问题来自于 GO 在将内存归还给操作系统时的内存释放策略,详情见官方 issues[2],以下做简单介绍。

    GO 内存释放策略

    不同策略的释放机制

    MADV_DONTNEED:内核将会在合适的时机去释放内存,但进程的 RSS(常驻内存)将会立即减少。如果再次申请内存,内核会重新分配一块新的空间。

    MADV_FREE:只能在 linux 内核版本 4.5 以上才能使用,此操作理论上只是打了一个标记位,只有在内核感觉到内存压力的时候才会将这些打标记的内存回收掉,分配给其他进程使用。这个策略下进程的 RSS 不会立即减少。

    不同策略的实际差别

    理论上 MADV_FREE 效率要高一些,通过在页表中做标记的方式,延迟内存的分配和回收,可以提高内存管理的效率,毕竟内存的回收和分配都是会消耗系统性能的;

    导致的 RSS 指标变化 MADV_DONTNEED 会导致进程 RSS 会有明显的下降;MADV_FREE 会导致进程 RSS 平稳在高峰,不会得到立即释放;

    不同 GO 版本的释放策略

    在 GO1.12 之前,默认均选择的 MADV_DONTNEED 策略进行内存回收

    在 GO1.12~GO1.15,官方默认选择 MADV_FREE 策略进行内存回收

    在 GO1.16 及之后,又改回了 MADV_DONTNEED 策略进行回收内存

    在 GO1.12~GO1.15 且内核版本 4.5 以上,mem.rss 指标已经无法准确观测服务内存占用

    解决方法

    不解决,对程序性能有利,但是会降低一些可观测性

    以下任一方法可以解决,但会损失一定性能 把 export GODEBUG=madvdontneed=1 写进服务 control.sh 脚本

    升级 GO 版本至 1.16 及以上

    使用 GODEBUG=madvdontneed=1 强制回退使用 MADV_DONTNEED,没有再出现内存泄漏问题。

    参考

    Go程序内存假泄漏是怎么回事 ?

    https://zhuanlan.zhihu.com/p/408573636

    Golang环境变量之GODEBUG

    https://blog.haohtml.com/archives/21778

    压测后go服务内存暴涨

    http://soiiy.com/go/17114.html

    Go 1.12 关于内存释放的一个改进

    https://cloud.tencent.com/developer/article/1489460

    相关文章

      网友评论

          本文标题:【golang】特定版本Golang应用内存泄露问题处理

          本文链接:https://www.haomeiwen.com/subject/xffaertx.html