老手也常误用!详解 Go channel 内存泄漏问题

一说到 go channel,很多人会使用“优秀”“哲学”这些词汇来描述。殊不知,go channel 恐怕还是 golang 中最容易造成问题的特性之一。很多情况下,我们使用 go channel 时,常常以为可以关闭 channel,但实际上却没有关闭,这就是导致 go channel 内存泄漏的元凶。

阅读本文前要求读者熟悉 go channel 的基本知识。如果你不够了解 go channel,那么可以先阅读《新手使用 go channel 需要注意的问题》。本文会默认你已经了解相关内容。

情境一:select-case 误用导致的内存泄露

废话说少,先看代码。

func TestLeakOfMemory(t *testing.T) {
   
   fmt.Println("NumGoroutine:", runtime.NumGoroutine())
   chanLeakOfMemory()
   time.Sleep(time.Second * 3) // 等待 goroutine 执行,防止过早输出结果
   fmt.Println("NumGoroutine:", runtime.NumGoroutine())
}

func chanLeakOfMemory() {
   
   errCh := make(chan error) // (1)
   go func() {
    // (5)
      time.Sleep(2 * time.Second)
      errCh <- errors.New("chan error") // (2)
      fmt.Println("finish sending")
   }()

   var err error
   select {
   
   case <-time.After(time.Second): // (3) 大家也经常在这里使用 <-ctx.Done()
      fmt.Println("超时")
   case err = <-errCh: // (4)
      if err != nil {
   
         fmt.Println(err)
      } else {
   
         fmt.Println(nil)
      }
   }
}

执行代码(需注意,使用测试和 main 线程执行的输出略有不同)

大家认为输出的结果是什么?正确的输出结果如下:

NumGoroutine: 2
超时
NumGoroutine: 3

这是 go channel 导致内存泄漏的经典场景。根据输出结果(开始有两个 goroutine,结束时有三个 goroutine),我们可以知道,直到测试函数结束前,仍有一个 goroutine 没有退出。原因是由于 (1) 处创建的 errCh 是不含缓存队列的 channel,如果 channel 只有发送方发送,那么发送方会阻塞;如果 channel 只有接收方

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值