有天在写代码的时候,正好在写一个定时任务把查询信息添加到redis,被组长臭骂一顿,你就这样写代码的??? 我打包放在服务器中运行测试一下发现内存疯狂激增 源代码
for {
select {
case v := <- chs:
fmt.Printf("print:%v\n", v)
case <- time.After(c.Timeouot):
... //get info to add redis
}
}
就是这么简单的代码却会导致内存泄漏。 点击time.After查看源码,发现以下注释------
// After waits for the duration to elapse and then sends the current time
// on the returned channel.
// It is equivalent to NewTimer(d).C.
// The underlying Timer is not recovered by the garbage collector
// until the timer fires. If efficiency is a concern, use NewTimer
// instead and call Timer.Stop if the timer is no longer needed.
翻译一下:在计时器触发之前,垃圾收集器不会回收Timer - 如果考虑效率,需要使用NewTimer替代
在select里面虽然我们没有执行到time.After,但是这个对象已经初始化了,依然在时间堆里面,定时任务未到期之前,是不会被gc清理的。
time.After虽然调用的是timer定时器,但是他没有使用time.Reset()方法再次激活定时器,所以每一次都是新创建的实例,才会造成的内存泄漏
** 解决方案**
我们使用NewTimer创建定时器,再加上time.Reset每次重新激活定时器,即可完美解决问题。
func Add() {
delay := time.NewTimer(3 * time.Minute)
defer delay.Stop()
for {
delay.Reset(3 * time.Minute)
select {
case v := <- chs:
fmt.Printf("print:%v\n", v)
case <- delay.C:
... //get info to add redis
}
}
}
```
```
//记得一定要使用Reset重置定时器,如果不重置,那么定时器还是从创建的时候开始计算时间流逝。
//使用了Reset之后,每次都从当前Reset的时间开始算。