为什么这个 golang 程序会导致内存泄漏?

huangapple go评论67阅读模式
英文:

Why does this golang program create a memory leak?

问题

我正在尝试理解并发性和goroutine,并对以下实验代码有几个问题:

  1. 为什么会导致内存泄漏?我以为goroutine末尾的返回语句会清理与其关联的内存。
  2. 为什么我的循环几乎从不达到999?实际上,当我将输出写入文件并研究输出时,我注意到它很少打印两位数的整数;第一次打印"99"是在第2461行,而打印"999"是在第6120行。这种行为对我来说是意外的,这显然意味着我对goroutine调度的情况并不真正了解。

免责声明:

小心运行下面的代码,如果你不在几秒钟后停止它,它可能会导致系统崩溃!

代码:

package main

import (
	"fmt"
	"sync"
)

func main() {
  var wg sync.WaitGroup
	for {
	  // 生成四个工作goroutine
	  spawnWorkers(4, wg)
	  // 等待工作goroutine完成
	  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)
	}
}

请注意,这只是代码的翻译部分,不包括回答你的问题。

英文:

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 (
	&quot;fmt&quot;
	&quot;sync&quot;
)

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 &lt; max; n++ {
    	wg.Add(1)
		go func() {
			defer wg.Done()
			f(n)
		  return
		}()
	}	
}

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

答案1

得分: 2

感谢Tim Cooper、JimB和Greg的有益评论。下面是修正后的代码供参考。

两个修复的地方是通过引用传递WaitGroup,修复了内存泄漏,并且正确地将n传递给匿名的goroutine。

package main

import (
	"fmt"
	"sync"
)

func main() {
	var wg sync.WaitGroup
	for {
		// 生成四个工作goroutine
		spawnWorkers(4, &wg)
		// 等待工作goroutine完成
		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)
	}
}
英文:

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 (
	&quot;fmt&quot;
	&quot;sync&quot;
)

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

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

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

huangapple
  • 本文由 发表于 2017年2月14日 03:51:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/42212381.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定