dpog
dpog

Reputation: 49

Why does this golang program create a memory leak?

I am trying to understand concurrency and goroutines, and had a couple questions about the following experimental code:

  1. Why does it create a memory leak? I thought that a return at the end of the goroutine would allow memory associated with it to get cleaned up.
  2. Why do my loops almost never reach 999? In fact, when I output to a file and study the output, I notice that it rarely prints integers in double digits; the first time it prints "99" is line 2461, and for "999" line 6120. This behavior is unexpected to me, which clearly means I don't really understand what is going on with goroutine scheduling.

Disclaimer:

Be careful running the code below, it can crash your system if you don't stop it after a few seconds!

CODE

package main

import (
    "fmt"
    "sync"
)

func main() {
  var wg sync.WaitGroup
    for {
      // spawn four worker goroutines
      spawnWorkers(4, wg)
      // wait for the workers to finish
      wg.Wait()
    }
}

func spawnWorkers(max int, wg sync.WaitGroup) {
  for n := 0; n < max; n++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            f(n)
          return
        }()
    }   
}

func f(n int) {
    for i := 0; i < 1000; i++ {
      fmt.Println(n, ":", i)
    }
}

Upvotes: 1

Views: 1950

Answers (1)

dpog
dpog

Reputation: 49

Thanks to Tim Cooper, JimB, and Greg for their helpful comments. The corrected version of the code is posted below for reference.

The two fixes were to pass in the WaitGroup by reference, which fixed the memory leak, and to pass n correctly into the anonymous goroutine, and

package main

import (
    "fmt"
    "sync"
)

func main() {
  var wg sync.WaitGroup
    for {
      // spawn four worker goroutines
      spawnWorkers(4,&wg)
      // wait for the workers to finish
      wg.Wait()
    }
}

func spawnWorkers(max int, wg *sync.WaitGroup) {
  for n := 0; n < max; n++ {
        wg.Add(1)
        go func(n int) {
            defer wg.Done()
            f(n)
          return
        }(n)
    }   
}

func f(n int) {
    for i := 0; i < 1000; i++ {
      fmt.Println(n, ":", i)
    }
}

Upvotes: 2

Related Questions