定时器中的内存泄漏

周期定时器

func main() {
    timer := time.NewTicker(1 * time.Second)  //NewTicker是周期定时器,NewTimer是普通定时器只能用一次
    for {
        <-timer.C
        fmt.Println("good")
    }
}

定时器

    time.Sleep(time.Second)
    //2Timer.C
    myTimer := time.NewTimer(time.Second * 2) //创建定时器,指定定时时长
    nowTime := <-myTimer.C                    //定时满,系统自动写入时间
    fmt.Println("现在时间", nowTime)
    //3 time.After
    nowTime = <-time.After(time.Second * 2)
    fmt.Println("现下时间", nowTime)

    //定时器的停止和重置
    myTimer2 := time.NewTimer(time.Second * 3)
    go func() {
        <-myTimer2.C
        fmt.Println("子go程,定时完毕")
    }()
    myTimer2.Stop() //停止
    myTimer.Reset(1 * time.Second) //重置

下面的写法会造成内存泄漏

package main

import (
    "time"
)

func main() {
    ch := make(chan int, 10)

    go func() {
        var i= 1
        for {
            i++
            ch <- i
        }
    }()

    for {
        select {
        case x := <-ch:
            println(x)
        case <-time.After(3 * time.Minute):
            println(time.Now().Unix())
        }
    }
}
`在for循环每次select的时候,都会实例化一个一个新的定时器。该定时器在3分钟后,才会被激活,但是激活后已经跟select无引用关系,被gc给清理掉。
换句话说,被遗弃的time.After定时任务还是在时间堆里面,定时任务未到期之前,是不会被gc清理的。
也就是说每次循环实例化的新定时器对象需要3分钟才会可能被GC清理掉,如果我们把上面复现代码中的3分钟改小点,改成10秒钟,通过top命令会发现大概10秒钟后,该程序占用的内存增长到1.05G后基本上就不增长了`
下面的代码是解决方案
package main

import (
    "time"
    )

func main()  {
    ch := make(chan int, 10)

    go func() {
        for {
            ch <- 100
        }
    }()

    idleDuration := 3 * time.Minute
    idleDelay := time.NewTimer(idleDuration) //综上,在go代码中,在for循环里不要使用select + time.After的组合,可以使用time.NewTimer替代
    defer idleDelay.Stop() //记得要关闭定时器

    for {
        idleDelay.Reset(idleDuration)

        select {
            case x := <- ch:
                println(x)
            case <-idleDelay.C:
                return
            }
    }
}




原文地址:https://www.cnblogs.com/hualou/p/12069705.html