对于一个channel来说,如果没有任何goroutine引用,gc会对其进行回收,不会引起内存泄露。
而当goroutine处于接收或发送阻塞状态,channel处于空或满状态时,一直得不到改变,gc则无法回收这类一直处于等待队列中的goroutine,引起内存泄露。
引起内存泄露的几种情况:
- 发送端channel满了,没有接收端
- 接收端channel为空,没有发送端
- channel未初始化
情形1:
channel缓存队列已满,接收者goroutine退出,发送者阻塞且所在goroutine无法退出
func main(){
channel := make(chan int, 5)
//sender
go func() {
for i:=0; i<10; i++{
channel <- i //阻塞
time.Sleep(10 * time.Millisecond)
fmt.Println("sender count:", i)
}
fmt.Println("sender exit.")
}()
//receiver
go func() {
i := <- channel //接收一个退出
fmt.Println("receive:", i)
fmt.Println("receiver exit.")
}()
for{
time.Sleep(10 * time.Millisecond)
}
}
情形2:
channel缓存队列为空,发送者goroutine退出,接收者阻塞且所在goroutine无法退出
func main(){
channel := make(chan int, 5)
//sender
go func() {
channel <- 1 //发一个退出
fmt.Println("sender exit.")
}()
//receiver
go func() {
for i:=0; i<10; i++ {
i := <-channel //阻塞
fmt.Println("receive:", i)
}
fmt.Println("receiver exit.")
}()
for{
time.Sleep(10 * time.Millisecond)
}
}
解决:
加一个通知另一段退出的stopChan
func main(){
channel := make(chan int, 5)
stopChan := make(chan struct{})//无缓冲的channel作为信号量
//sender
go func() {
for i:=0; i<10; i++{
select {
case <-stopChan: //收到close信号,退出。多个sender时,每个goroutine也都能收到
fmt.Println("sender exit.")
return
case channel <- i:
time.Sleep(10 * time.Millisecond)
fmt.Println("sender count:", i)
}
}
fmt.Println("sender exit.")
}()
//receiver
go func() {
i := <- channel
close(stopChan)
fmt.Println("receive:", i)
fmt.Println("receiver exit.")
}()
for{
time.Sleep(10 * time.Millisecond)
}
}
备注:
从一个关闭的channel仍能读出数据,当缓冲队列还有数据时,会返回正常值;缓冲队列为空时,则返回channel已关闭。
向一个关闭的channel发送数据则会报Panic
参考:
https://juejin.cn/post/7033711399041761311
https://www.cnblogs.com/ricklz/p/11262069.html