Question

Consider the following code as a simplified example:

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
    }
}

The function provide creates a go routine printer that prints the data provide generates.

My question is, what happens after provide returns and printer starts blocking on the empty channel. Will the go routine leak, as there is no further reference to c or will the garbage collector catch this case and dispose both the go routine and c?

If it is indeed the case that this kind of code causes a memory leak, what strategies can I do to prevent such a memory leak from happening?

Was it helpful?

Solution

Close the channel. Reading from a closed channel always succeeds, returning a respective zero value. The optional second boolean returned value indicates validity of the first value.

Receive operator:

A receive expression used in an assignment or initialization of the form

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

yields an additional result of type bool reporting whether the communication succeeded. The value of ok is true if the value received was delivered by a successful send operation to the channel, or false if it is a zero value generated because the channel is closed and empty.

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)
}

OTHER TIPS

Try the following program to verify that this indeed leaks memory. Please notice that this program eats up your RAM rather quickly; be prepared to kill it.

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()
    }
}

In order to resolve the leak, close a channel that is referenced by the now orphaned go routine. The runtime notices that a Go routine reading from only closed channels will never continue and proceeds to free it. The fixed code looks like this:

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()
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top