• 【涨姿势】原来golang的case <-time.After(xxx)还有这样的坑


    偶然看到这样一篇文章:《使用 pprof 排查 Golang 内存泄露》https://www.toutiao.com/i6881796351139676680/
    最后一段让我很疑惑:

    修改 for ... select ... time.After 造成的内存泄露
    原来程序中存在如下代码:
    
    for {
    		select {
    		case a := <-chanA:
    			...		case b := <-chanB:
    			....		case <-time.After(20*time.Minutes):
    			return nil, errors.New("download timeout")
    	} 
    time.After 就是封装了一层的 NewTimer, time.After 的源码:
    
    func After(d Duration) <-chan Time {
    	return NewTimer(d).C
    }
    修复该错误, 只调用一次 NewTimer:
    
    downloadTimeout := time.NewTimer(20 * time.Minute)
    defer downloadTimeout.Stop()
    for {
    		select {
    		case a := <-chanA:
    			...		case b := <-chanB:
    			....		case <-downloadTimeout.C:
    			return nil, errors.New("download timeout")
    	} 
    

    连官网的例子都是:case <-time.After(xxx), 为什么这里就出现内存泄露了?
    感谢yifhao同学耐心细致的讲解:

    • 20 * time.Minute 这个时间太长了
    • 如果1s钟调用这里1000次, 那就可能会同时存在20601000个定时事件
    • 退出select的作用域的时候,time.After(20*time.Minutes)产生的对象不会被GC,走到runtime里的timer调度去了
    • 退出作用域不会被释放. 定时事件作用域在timer的调度器上去了。定时器是全局对象. 调度到了才被释放。
    • 因此,时间短,且循环不频繁的情况下,case <-time.After(xxx)这个写法还是可以的
  • 相关阅读:
    Spring IoC 容器和 AOP
    MySQL 锁与事务控制
    MySQL 存储引擎的选择
    如何理解MySQL 索引最左前缀原则
    MySQL 索引
    Java 线程池
    Java多线程 ReentrantLock、Condition 实现生产者、消费者协作模式
    Java多线程并发中 CAS 的使用与理解
    Java多线程中协作机制
    Mysql-SQL生命周期-show profile
  • 原文地址:https://www.cnblogs.com/ahfuzhang/p/13805561.html
Copyright © 2020-2023  润新知