写代码时,经常会使用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来计算的。