GO协程泄漏

协程泄漏原因

  • 协程阻塞,未能如期结束
  • 协程阻塞最常见的原因都跟channel有关
  • 由于每个协程都要占用内存,所以协程泄漏也会导致内存泄漏
  • 示例代码
    • fooChan := make(chan struct{}) // 协程泄漏地方
    • 因为没有使用缓冲区,则一直堵塞在fooChan <- struct{}{}
package main import ( "context" "fmt" "runtime" "time" ) func foo() { time.Sleep(100 * time.Millisecond) return } func handleRequest(timeout time.Duration) bool { ctx, cancle := context.WithCancel(context.Background()) // make(chan struct{}, 1)则不泄漏 fooChan := make(chan struct{}) // 协程泄漏地方 go func() { foo() fooChan <- struct{}{} }() go func() { time.Sleep(timeout) cancle() // 调用,关闭ctx }() select { case <-fooChan: fmt.Println("foo finish") case <-ctx.Done(): // 接口返回超时 // fmt.Println("timeout") } return true } func main() { ticker := time.NewTicker(1 * time.Second) defer ticker.Stop() go func() { for { <-ticker.C fmt.Printf("go routine number %d\n", runtime.NumGoroutine()) } }() for { handleRequest(50 * time.Millisecond) } }

协程泄漏排查

  • 重点代码
import ( "net/http" _ "net/http/pprof" ) func main() { go func() { if err := http.ListenAndServe("localhost:8080", nil); err != nil { panic(err) } }() }
  • 访问地址:http://127.0.0.1:8080/debug/pprof/goroutine?debug=1

    • total 协程数量
    • 查看哪部分使用的协程数量比较多
    goroutine profile: total 507 -------------------------------------一共507协程 498 @ 0x43af15 0x40556b 0x405325 0x73fcb9 0x4692d1 ----------------------------------这里使用了498个协程 # 0x73fcb8 main.handleRequest.func1+0x48 E:/GO_project/timeout.go:23
  • 示例代码

    • fooChan := make(chan struct{}) // 协程泄漏地方
    • 因为没有使用缓冲区,则一直堵塞在fooChan <- struct{}{}
package main import ( "context" "fmt" "net/http" _ "net/http/pprof" "runtime" "time" ) func foo() { time.Sleep(100 * time.Millisecond) return } func handleRequest(timeout time.Duration) bool { ctx, cancle := context.WithCancel(context.Background()) // make(chan struct{}, 1)则不泄漏 fooChan := make(chan struct{}) // 协程泄漏地方 go func() { foo() fooChan <- struct{}{} }() go func() { time.Sleep(timeout) cancle() // 调用,关闭ctx }() select { case <-fooChan: fmt.Println("foo finish") case <-ctx.Done(): // 接口返回超时 // fmt.Println("timeout") } return true } func main() { go func() { if err := http.ListenAndServe("localhost:8080", nil); err != nil { panic(err) } }() ticker := time.NewTicker(1 * time.Second) defer ticker.Stop() go func() { for { <-ticker.C fmt.Printf("go routine number %d\n", runtime.NumGoroutine()) } }() for { handleRequest(50 * time.Millisecond) } }

命令行终端命令

  • go tool pprof http://0.0.0.0:8080/debug/pprof/goroutine
  • Saved profile in 保存路径
    • go tool pprof --http=:8081 C:\Users\pprof\pprof.goroutine.001.pb.gz
    • 访问http://localhost:8081,可视化SVG
  • Type 当前类型
  • Time 当前时间
  • top 查看当前情况
    • 越往下,越能体现底层原因
  • list 函数名 // 查看代码每一行开辟的goroutine数量
    • 5225 23: fooChan <- struct{}{} // 开辟了5225个goroutine
  • traces 打印调用堆栈
  • web 生成SVG文件,可视化图
$ go tool pprof http://0.0.0.0:8080/debug/pprof/goroutine Fetching profile over HTTP from http://0.0.0.0:8080/debug/pprof/goroutine Saved profile in C:\Users\pprof\pprof.goroutine.001.pb.gz Type: goroutine Time: Oct 7, 2021 at 12:23am (CST) Entering interactive mode (type "help" for commands, "o" for options) (pprof)top Showing nodes accounting for 5233, 100% of 5234 total Dropped 44 nodes (cum <= 26) flat flat% sum% cum cum% 5233 100% 100% 5233 100% runtime.gopark 0 0% 100% 5227 99.87% main.handleRequest.func1 0 0% 100% 5225 99.83% runtime.chansend 0 0% 100% 5225 99.83% runtime.chansend1 (pprof)list main.handleRequest.func1 Total: 5234 ROUTINE ======================== main.handleRequest.func1 in E:\GO_project\k8sdev\timeout.go 0 5227 (flat, cum) 99.87% of Total . . 17:func handleRequest(timeout time.Duration) bool { . . 18: ctx, cancle := context.WithCancel(context.Background()) . . 19: // make(chan struct{}, 1)则不泄漏 . . 20: fooChan := make(chan struct{}) // 协程泄漏地方 . . 21: go func() { . 2 22: foo() . 5225 23: fooChan <- struct{}{} . . 24: }() . . 25: . . 26: go func() { . . 27: time.Sleep(timeout) . . 28: cancle() // 调用,关闭ctx (pprof)traces Type: goroutine Time: Oct 7, 2021 at 12:23am (CST) -----------+------------------------------------------------------- 5225 runtime.gopark runtime.chansend runtime.chansend1 main.handleRequest.func1 -----------+------------------------------------------------------- 2 runtime.gopark time.Sleep main.foo main.handleRequest.func1 -----------+------------------------------------------------------- 1 runtime.gopark runtime.chanrecv runtime.chanrecv1 main.main.func2 -----------+------------------------------------------------------- 1 runtime.gopark runtime.netpollblock internal/poll.runtime_pollWait internal/poll.(*pollDesc).wait internal/poll.(*ioSrv).ExecIO internal/poll.(*FD).Read net.(*netFD).Read net.(*conn).Read net/http.(*connReader).backgroundRead -----------+------------------------------------------------------- 1 runtime.gopark runtime.netpollblock internal/poll.runtime_pollWait internal/poll.(*pollDesc).wait internal/poll.(*ioSrv).ExecIO internal/poll.(*FD).Read net.(*netFD).Read net.(*conn).Read net/http.(*connReader).Read bufio.(*Reader).fill bufio.(*Reader).ReadSlice bufio.(*Reader).ReadLine net/textproto.(*Reader).readLineSlice net/textproto.(*Reader).ReadLine net/http.readRequest net/http.(*conn).readRequest net/http.(*conn).serve -----------+------------------------------------------------------- 1 runtime.gopark runtime.netpollblock internal/poll.runtime_pollWait internal/poll.(*pollDesc).wait internal/poll.(*ioSrv).ExecIO internal/poll.(*FD).acceptOne internal/poll.(*FD).Accept net.(*netFD).accept net.(*TCPListener).accept net.(*TCPListener).Accept net/http.(*Server).Serve net/http.(*Server).ListenAndServe net/http.ListenAndServe main.main.func1 -----------+------------------------------------------------------- 1 runtime.gopark runtime.selectgo main.handleRequest main.main runtime.main -----------+------------------------------------------------------- 1 runtime.gopark time.Sleep main.handleRequest.func2 -----------+------------------------------------------------------- 1 runtime/pprof.writeRuntimeProfile runtime/pprof.writeGoroutine runtime/pprof.(*Profile).WriteTo net/http/pprof.handler.ServeHTTP net/http/pprof.Index net/http.HandlerFunc.ServeHTTP net/http.(*ServeMux).ServeHTTP net/http.serverHandler.ServeHTTP net/http.(*conn).serve -----------+------------------------------------------------------- (pprof)

__EOF__

本文作者每天记录一点点,日积月累 - rxg456
本文链接https://www.cnblogs.com/rxg456/p/16221859.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   rxg456  阅读(255)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示