写代码时,经常会使用context.WithTimeout,传入某个goroutine。不小心会忘记执行defer cancel()。从而导致context泄露。具体泄露多少内存呢?
我写了一个程序来计算大小:
- package main
-
- import (
- "context"
- "fmt"
- "runtime"
- "time"
- "unsafe"
- "github.com/DmitriyVTitov/size"
- )
-
-
- func bToMb(b uint64) uint64 {
- return b / 1024 / 1024
- }
-
- func PrintMemUsage() {
- var m runtime.MemStats
- runtime.ReadMemStats(&m)
- // For info on each, see: https://golang.org/pkg/runtime/#MemStats
- fmt.Printf("Alloc = %v MiB", bToMb(m.Alloc))
- fmt.Printf("\tTotalAlloc = %v MiB", bToMb(m.TotalAlloc))
- fmt.Printf("\tSys = %v MiB", bToMb(m.Sys))
- fmt.Printf("\tNumGC = %v\n", m.NumGC)
- }
-
- func main() {
- PrintMemUsage()
- ctx, cancle := context.WithTimeout(context.Background(), time.Duration(time.Second*1))
- defer cancle()
- fmt.Println("sizeof(ctx) :", unsafe.Sizeof(ctx))
- fmt.Println(size.Of(ctx))
-
- //time.Sleep(time.Second * 20)
- fmt.Println("sleep finish")
-
- len := 1024 * 10
- for i := 0; i < len ; i ++ {
- context.WithTimeout(context.Background(), time.Duration(time.Second*10))
- }
- PrintMemUsage()
-
- }
执行结果如下:
- Alloc = 0 MiB TotalAlloc = 0 MiB Sys = 8 MiB NumGC = 0
- sizeof(ctx) : 16
- 254
- sleep finish
- Alloc = 2 MiB TotalAlloc = 2 MiB Sys = 8 MiB NumGC = 0
可以发现使用unsafe.Sizeof计算出来的大小是不对的。
系统增加了2M内存,即2*1024KB,2*1024/(1024 *10) = 0.2KB = 0.2*1024 = 204B接近254字节。可能是数字太小导致误差太大。我们循环调成 1024*1024。重新执行一遍,结果如下:
- Alloc = 0 MiB TotalAlloc = 0 MiB Sys = 8 MiB NumGC = 0
- sizeof(ctx) : 16
- 254
- sleep finish
- Alloc = 215 MiB TotalAlloc = 248 MiB Sys = 236 MiB NumGC = 7
增加了 240M内存,即每个大小为240字节,差了14字节。
在代码中加上debug.SetGCPercent(-1) 代码,禁用gc。代码如下:
- package main
-
- import (
- "context"
- "fmt"
- "runtime"
- "runtime/debug"
- "time"
- "unsafe"
- "github.com/DmitriyVTitov/size"
- )
-
-
- func bToMb(b uint64) uint64 {
- return b / 1024 / 1024
- }
-
- func PrintMemUsage() {
- var m runtime.MemStats
- runtime.ReadMemStats(&m)
- // For info on each, see: https://golang.org/pkg/runtime/#MemStats
- fmt.Printf("Alloc = %v MiB", bToMb(m.Alloc))
- fmt.Printf("\tTotalAlloc = %v MiB", bToMb(m.TotalAlloc))
- fmt.Printf("\tSys = %v MiB", bToMb(m.Sys))
- fmt.Printf("\tNumGC = %v\n", m.NumGC)
- }
-
- func main() {
- debug.SetGCPercent(-1)
- PrintMemUsage()
- ctx, cancle := context.WithTimeout(context.Background(), time.Duration(time.Second*1))
- defer cancle()
- fmt.Println("sizeof(ctx) :", unsafe.Sizeof(ctx))
- fmt.Println(size.Of(ctx))
-
- //time.Sleep(time.Second * 20)
- fmt.Println("sleep finish")
-
- len := 1024*1024
- for i := 0; i < len ; i ++ {
- context.WithTimeout(context.Background(), time.Duration(time.Second*30))
- }
- PrintMemUsage()
-
- }
运行结果如下:
- Alloc = 0 MiB TotalAlloc = 0 MiB Sys = 8 MiB NumGC = 0
- sizeof(ctx) : 16
- 254
- sleep finish
- Alloc = 247 MiB TotalAlloc = 247 MiB Sys = 267 MiB NumGC = 0
这样误差就更小了。
说明"github.com/DmitriyVTitov/size" 是可信的。此工具是基于go的binary.Size来计算的。