垃圾收集器会收集永远不会继续的Go例程吗?

时间:2013-05-16 15:06:52

标签: memory-leaks go goroutine

将以下代码视为简化示例:

func printer(c <-chan int) {
    for {
        fmt.Print(<-c)
    }
}

func provide() {
    c := make(chan int)

    go printer(c)

    for i := 1; i <= 100; i++ {
        c <- i
    }
}

函数provide创建一个go例程printer,用于打印provide生成的数据。

我的问题是,provide返回后会发生什么,printer会在空信道上开始阻塞。 go例程是否会泄漏,因为没有对c的进一步引用,或者垃圾收集器是否会抓住这个案例并处理go例程和c

如果确实存在此类代码导致内存泄漏的情况,我可以采取哪些策略来防止此类内存泄漏?

2 个答案:

答案 0 :(得分:10)

关闭频道。从封闭通道读取总是成功,返回相应的零值。可选的第二个布尔值返回值表示第一个值的有效性。

Receive operator

  

在表单的赋值或初始化中使用的接收表达式

x, ok = <-ch
x, ok := <-ch
var x, ok = <-ch
  

产生类型bool的其他结果,报告通信是否成功。如果接收到的值是通过成功的发送操作传递给通道,则ok的值为true;如果由于通道关闭且为空而产生零值,则返回false。

func printer(c <-chan int) {
        for {
                v, ok := <-c
                if !ok { // chan closed
                        return
                }

                // v is valid
                fmt.Println(v)
        }
}

func provide() {
        c := make(chan int)

        go printer(c)

        for i := 1; i <= 100; i++ {
                c <- i
        }
        close(c)
}

答案 1 :(得分:0)

尝试以下程序来验证这确实泄漏了内存。请注意,这个程序很快就会占用你的RAM;准备杀了它。

package main

func worker(c <-chan int) {
    var i int

    for {
        i += <-c
    }
}

func wrapper() {
    c := make(chan int)

    go worker(c)

    for i := 0; i < 0xff; i++ {
        c <- i
    }
}

func main() {
    for {
        wrapper()
    }
}

为了解决泄漏问题,请关闭现在孤立的go例程引用的通道。运行时注意到仅从封闭通道读取的Go例程将永远不会继续并继续释放它。固定代码如下所示:

package main

func worker(c <-chan int) {
    var i int

    for {
        i += <-c
    }
}

func wrapper() {
    c := make(chan int)
    defer close(c) // fix here

    go worker(c)

    for i := 0; i < 0xff; i++ {
        c <- i
    }
}

func main() {
    for {
        wrapper()
    }
}