Skip to content

Instantly share code, notes, and snippets.

@kawasin73
Created August 23, 2017 09:56
Show Gist options
  • Save kawasin73/a6770025d06216f634628472a36be1d4 to your computer and use it in GitHub Desktop.
Save kawasin73/a6770025d06216f634628472a36be1d4 to your computer and use it in GitHub Desktop.
goroutine leaks

goroutine がリークする

goroutineは終了しているものの、goroutineがGCされていないようです。

即座にgoroutineが終了(つまり同時に存在するgoroutineの数が多くない)する場合は、ちゃんとGCするみたい。 大量のgoroutineが同時に存在するとGCできずにリークするみたい。

ヒープとスタック

routine3

スタックにallocateされている スタックにallocateされたものは、goroutineがGCされていないため余分にリークしている。(47MB)

$ go build -gcflags -m main.go 
# command-line-arguments
./main.go:56: can inline subroutine4
./main.go:46: wghoge escapes to heap
./main.go:44: routine3 make([]byte, 1000) does not escape // <-ここ
./main.go:20: func literal escapes to heap
./main.go:20: func literal escapes to heap
./main.go:23: "start" escapes to heap
./main.go:25: wghoge escapes to heap
./main.go:28: "num goroutine: " escapes to heap
./main.go:28: runtime.NumGoroutine() escapes to heap
./main.go:29: wghoge escapes to heap
./main.go:30: "done" escapes to heap
./main.go:31: "num goroutine: " escapes to heap
./main.go:31: runtime.NumGoroutine() escapes to heap
./main.go:21: http.ListenAndServe("localhost:6060", nil) escapes to heap
./main.go:23: test ... argument does not escape
./main.go:28: test ... argument does not escape
./main.go:30: test ... argument does not escape
./main.go:31: test ... argument does not escape
./main.go:21: test.func1 ... argument does not escape
./main.go:35: wghoge escapes to heap
./main.go:40: wghoge escapes to heap
./main.go:56: subroutine4 buf does not escape
./main.go:50: make([]byte, 1000) escapes to heap
./main.go:53: wghoge escapes to heap

routine4

[]byteはヒープにallocateされている。 ヒープは正しくGCされている。(29MB)

$ go build -gcflags -m main.go 
# command-line-arguments
./main.go:56: can inline subroutine4
./main.go:56: subroutine4 buf does not escape
./main.go:50: make([]byte, 1000) escapes to heap // <- ここ
./main.go:53: wghoge escapes to heap
./main.go:20: func literal escapes to heap
./main.go:20: func literal escapes to heap
./main.go:23: "start" escapes to heap
./main.go:25: wghoge escapes to heap
./main.go:28: "num goroutine: " escapes to heap
./main.go:28: runtime.NumGoroutine() escapes to heap
./main.go:29: wghoge escapes to heap
./main.go:30: "done" escapes to heap
./main.go:31: "num goroutine: " escapes to heap
./main.go:31: runtime.NumGoroutine() escapes to heap
./main.go:21: http.ListenAndServe("localhost:6060", nil) escapes to heap
./main.go:23: test ... argument does not escape
./main.go:28: test ... argument does not escape
./main.go:30: test ... argument does not escape
./main.go:31: test ... argument does not escape
./main.go:21: test.func1 ... argument does not escape
./main.go:35: wghoge escapes to heap
./main.go:40: wghoge escapes to heap
./main.go:46: wghoge escapes to heap
./main.go:44: routine3 make([]byte, 1000) does not escape
package main
import (
"log"
"net/http"
_ "net/http/pprof"
"runtime"
"sync"
"time"
"runtime/debug"
)
func main() {
test()
runtime.GC()
debug.FreeOSMemory()
time.Sleep(3600 * time.Second)
}
var wghoge sync.WaitGroup
func test() {
go func() {
log.Println(http.ListenAndServe("localhost:6060", nil))
}()
log.Println("start")
for i := 0; i < 10000; i++ {
wghoge.Add(1)
go routine3()
}
log.Println("num goroutine: ", runtime.NumGoroutine())
wghoge.Wait()
log.Println("done")
log.Println("num goroutine: ", runtime.NumGoroutine())
}
func routine1() {
wghoge.Done()
}
func routine2() {
time.Sleep(time.Second)
wghoge.Done()
}
func routine3() {
_ = make([]byte, 10000)
time.Sleep(time.Second)
wghoge.Done()
}
func routine4() {
hello := make([]byte, 1000)
go subroutine4(hello)
time.Sleep(time.Second)
wghoge.Done()
}
func subroutine4(buf []byte) {
buf[0] = 1
}

結果

routine1

$ go run main.go 
2017/08/23 18:08:29 start
2017/08/23 18:08:29 num goroutine:  21
2017/08/23 18:08:29 done
2017/08/23 18:08:29 num goroutine:  4

__ __ _goroutine gc _ issue__153

routine2

$ go run main.go 
2017/08/23 18:09:32 start
2017/08/23 18:09:32 num goroutine:  10003
2017/08/23 18:09:33 done
2017/08/23 18:09:33 num goroutine:  3

__ _

routine3

$ go run main.go 
2017/08/23 18:10:32 start
2017/08/23 18:10:32 num goroutine:  10003
2017/08/23 18:10:33 done
2017/08/23 18:10:33 num goroutine:  3

__ __ __1__goroutine gc _ issue__153

routine4

$ go run main.go 
2017/08/23 18:27:53 start
2017/08/23 18:27:53 num goroutine:  10004
2017/08/23 18:27:54 done
2017/08/23 18:27:54 num goroutine:  3

__ _

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment