goroutine泄露

这里所说的Go内存泄露是指goroutine泄露。如果你启动了1个goroutine,但并没有符合预期的退出,直到程序结束,此goroutine才退出,这种情况就是goroutine泄露。在此之前先来认识一下pprof,pprof是Go的性能分析工具,在程序运行过程中,可以记录程序的运行信息,可以是CPU使用情况、内存使用情况、goroutine运行情况等。

Go已经有一个封装好的net/http/pprof,使用简单的几行命令,就可以开启pprof,记录运行信息,并且提供了Web服务。

如果一个存在的Go内存泄露情况如下:

http://xxxx/debug/pprof/

image-20211228111613802

allocs:所有过去内存分配的样本
block:导致同步阻塞的堆栈跟踪
cmdline:当前程序的命令行调用
goroutine:所有当前 goroutine 的堆栈跟踪
heap:活动对象的内存分配示例。 您可以指定 gc GET 参数以在获取堆样本之前运行 GC。
mutex:竞争互斥体持有者的堆栈跟踪
profile:CPU 配置文件。 您可以在 seconds GET 参数中指定持续时间。 获取配置文件后,使用 go tool pprof 命令调查配置文件。
threadcreate:导致创建新操作系统线程的堆栈跟踪
trace:当前程序执行的轨迹。 您可以在 seconds GET 参数中指定持续时间。 获取跟踪文件后,使用 go tool trace 命令调查跟踪。

比如点一个cmdline,查看运行的命令,也许会包括账号密码。

/debug/pprof/cmdline?debug=1

image-20211228111836637

点击profile或者trace的时候会下载一个编译的文件,里面含有进程信息以及程序信息。使用如下命令查看,可以看到这是一个so文件。

go tool pprof .\profile
# go tool pprof http://xxx/profile

image-20211228112643219

查看进程函数占用,查看命令介绍可以使用help。

image-20211228113041465

也可以下载heap查看,需要删掉链接上自动带的debug=1。

image-20211228131632457

这个heap文件写的是什么

/debug/pprof/goroutine?debug=1

image-20211228133437841

大概能看出来的是有62个goroutine被挂起,不能退出。这里面有6个goroutine挂在了wss_client.go的104行。

image-20211228134321120

pprof分析

先安装一个graphviz:https://graphviz.gitlab.io/_pages/Download/Download_windows.html

go tool pprof --http=":8999" https://xxxx/debug/pprof/heap

image-20211228142010787

颜色越深越大的代表占用和耗时越多

image-20211228142130766

goroutine 泄露的场景

goroutine泄露的本质是channel阻塞,无法继续向下执行,导致此goroutine关联的内存都无法释放,进一步造成内存泄露。

  1. channel的读或者写:
    1. 无缓冲channel的阻塞通常是写操作因为没有读而阻塞
    2. 有缓冲的channel因为缓冲区满了,写操作阻塞
    3. 期待从channel读数据,结果没有goroutine写
  2. select操作,select里也是channel操作,如果所有case上的操作阻塞,goroutine也无法继续执行。

参考文章:

https://segmentfault.com/a/1190000019222661

https://segmentfault.com/a/1190000019644257





# web安全  

tocToc: