Go 内存泄漏排查两例

例1:Goroutine 泄漏

现象

NumGoroutine 指标持续上涨,且低峰期未下降,判断出现了 Goroutine 泄漏现象。

bb9cc2d0693075f7f2abb91d0acc1ac6.png
image.png

排查

  1. 通过访问线上服务 pprof 暴露出来的 HTTP 接口,拿到当前所有协程的堆栈信息;curl http://「ip:port」/debug/pprof/goroutine?debug=235470d4193057d8d6c1293b81833f0d3.png

  2. 发现存在大量存活时间超过上千分钟的 Goroutine,观察堆栈疑似是 http 连接未释放导致,通过对下图 net.sockets.tcp.inuse(正在使用的tcp socket数量)指标的观察进行了进一步的确认;01aee9abfe1c37554d3f7a62de1cae50.png

结论

http

下面以本次 case http 服务为例,做简单介绍:

  • 上游服务作为客户端使用了 http1.1 并且将连接设置为 keepalive;

  • 本服务作为服务端未设置 idletimeout 与 readtimeout;

当这两种情况同时发生时,如果上游持有对本服务的连接不进行释放,那么服务端会一直维持这个连接的存在,不进行回收,进而导致协程泄漏;

client上游客户端可能为 GO、Java 等,以下为 GO 语言 http 客户端的空闲连接超时设置;af3c1c35c339dfdc07d75ae57393f323.pngserver17665b9c76efea9b6be9af8640585eca.png解决39732ff6342df0fbb978e75364e239ce.png建议启动 http server 尽量用后者,前者虽然简单,但是服务不够健壮;

thrift

serverc5520bea33deba4d159dfc4c17795f20.png

3c12bdba165380759c3e135108d9e475.png
image.png
Tips

需要注意的一点是,这个 Goroutine 泄漏问题不止在 http 下会发生,在 thrift、grpc 中也是同样的道理,如果服务端不对连接设置 timeout,某些情况下就会被上游拖死。

Reference

一起 goroutine 泄漏问题的排查[1]

例2:内存居高不下

现象

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

排查

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

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

结论

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

GO 内存释放策略

(此节内容整理自 压测后go服务内存暴涨[3]

不同策略的释放机制

  • 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 及以上;

参考资料

[1]

一起 goroutine 泄漏问题的排查: https://zhuanlan.zhihu.com/p/100740270

[2]

issues: https://github.com/golang/go/issues/42330

[3]

压测后go服务内存暴涨: http://soiiy.com/go/17114.html

欢迎关注Go招聘公众号,获取Go专题大厂内推面经简历股文等相关资料可回复和点击导航查阅。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值