记一次go语言内存泄漏解决经历

    某个周一的上午,客户反应我们的APP设备列表不显示设备状态,实时视频也无法查看了。我登陆服务器查看了下上线服务器的日志,停留在凌晨2点多,ps查看了一下进程,也不存在了。由于客户着急使用软件,先把进程启动起来再说,至于原因和解决办法,可以从长计议。

    上线服务器我安装有听云sys监控,马上对这个上线服务的进程的CPU和内存的数据进行查看。果然,这个进程的内存每天都上涨30MB,直到内存无可用进程被系统杀死。

    我们的服务是go语言实现的,go语言是门支持内存自动回收的语言,凡是能退出的函数,函数内部申请的内存注定会被释放。函数外的常量总数比较固定,即使不被清理也不至于每天都上涨。通过go语言的pprof进行进程监控,goroutine的总数只上升不下降,导致内存消耗从启动时的20MB,涨到了62MB。

   点击goroutine 超链接进入goroutine的详细情况,查看哪个函数最多。是readLoop,writeLoop和handleLoop的Close函数go飞了。

    这个函数使用了sync.WaitGroup的Wait()函数,这个函数必须wait until all go-routines exited.才能继续向下进行。不能向下进行说明肯定至少有一个go-routines没有退出。于是我把三个go-routines 的函数退出时的日志都放开,发现TCP被动断开时,触发onNetClose导致的Close,readLoop,writeLoop和handleLoop 三个go-routines都能正常退出,而定时进程主动发起的Close,只有readLoop,writeLoop能退出,而handleLoop不能退出,最终导致每主动Close一次,readLoop,writeLoop和handleLoop三个go-routines的数值都会+1。

此图是TCP被动断开的日志:

此图是程序主动断开TCP的日志:

    两者都是调用的同一个Close方法,不知道为啥主动断开时handleLoop为什么接受不到通道传来的信息。而且writeLoop 接受的是相同的通道,但是writeLoop就能接收到通道消息退出。在这一块想了很久也没有得到解决办法,不但没有改好问题,还有一次修改直接把CPU飙到了105%。

    问题的解决办法有两种,一是消灭问题,二是绕开问题。消灭做不到,绕开总有办法吧。

    于是我重新梳理了代码的逻辑,用新的方式实现原有逻辑,保证新的方式不会出现问题即可,这样就可以把老的方式注释掉从而规避问题。这个上线服务器的逻辑是当TCP断开时,执行离线程序;同时每个TCP连接都有一个90S的定时任务,任务是检查最近的保活时间和当前时间的间隔,间隔过大则服务器主动断开TCP连接,从而触发离线逻辑。既然是处理的是超时,我们也可以把这个逻辑下沉到handleLoop里进行,每个保活包都会由handleLoop的select控制器进行处理,那么只要有保活包,两个select之间的间隔就不会超过保活周期,那可以给select增加一个超时的通信操作,当select超过这个配置的超时时间时,退出handleLoop,然后向readLoop 和 writeLoop 发送通道信息,一起退出后执行wait()之后的代码从而退出Close函数不再阻塞。

    通过验证,handleLoop 退出确实可以成功向readLoop 和 writeLoop 发送通道信息,触发readLoop 和 writeLoop 的退出。

    于是,对上线服务进行修改:1.注释掉每个连接生成时启动的定时任务;2.handleLoop的select 增加time.After的超时通道,收到消息则通过return退出handleLoop。

    修改完部署对内存和pprof进行观测,连续运行了三天Close的goroutine不会再出现,且整体goroutine不会出现持续增加。听云检测内存的变化,可以在25MB和30MB之前正常波动,不再出现持续增长的情况了。

    

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值