go 如何查看程序内存泄漏?

AI摘要
该内容为Go语言程序调试知识分享,通过一个模拟协程泄漏的示例程序,演示了如何使用pprof工具分析goroutine profile。内容详细解析了程序启动的HTTP服务器、pprof端点处理、网络监听、主协程以及一个无限循环的泄漏协程等不同goroutine组的堆栈信息和状态,属于典型的技术问题排查教程。
package main

import (
    "fmt"
    "net/http"
    _ "net/http/pprof" // 自动注册pprof的handler
    "time"
)

func main() {
    // 启动pprof的HTTP服务
    go func() {
        http.ListenAndServe("localhost:6060", nil)
    }()
    // 模拟协程泄漏
    go leakedGoroutine()

    // 保持程序运行
    select {}
}

func leakedGoroutine() {
    for {
        time.Sleep(time.Second)
        fmt.Println("泄漏的协程还在运行...")
    }
}

查看trace

```html

goroutine profile: total 7
2 @ 0xaf4b0e 0xab8c77 0xaf3ce5 0xb47f47 0xb487e5 0xb494bb 0xbb3aa5 0xbbea45 0xc927b9 0xc3f543 0xc3fdc9 0xc40025 0xc41165 0xca17f0 0xca180b 0xc934c7 0xc980f8 0xafc6a1
#    0xaf3ce4    internal/poll.runtime_pollWait+0x84        D:/Program Files/Go/src/runtime/netpoll.go:351
#    0xb47f46    internal/poll.(*pollDesc).wait+0x26        D:/Program Files/Go/src/internal/poll/fd_poll_runtime.go:84
#    0xb487e4    internal/poll.execIO+0x104            D:/Program Files/Go/src/internal/poll/fd_windows.go:177
#    0xb494ba    internal/poll.(*FD).Read+0x29a            D:/Program Files/Go/src/internal/poll/fd_windows.go:438
#    0xbb3aa4    net.(*netFD).Read+0x24                D:/Program Files/Go/src/net/fd_posix.go:55
#    0xbbea44    net.(*conn).Read+0x44                D:/Program Files/Go/src/net/net.go:194
#    0xc927b8    net/http.(*connReader).Read+0x158        D:/Program Files/Go/src/net/http/server.go:798
#    0xc3f542    bufio.(*Reader).fill+0x102            D:/Program Files/Go/src/bufio/bufio.go:113
#    0xc3fdc8    bufio.(*Reader).ReadSlice+0x28            D:/Program Files/Go/src/bufio/bufio.go:380
#    0xc40024    bufio.(*Reader).ReadLine+0x24            D:/Program Files/Go/src/bufio/bufio.go:409
#    0xc41164    net/textproto.(*Reader).readLineSlice+0xa4    D:/Program Files/Go/src/net/textproto/reader.go:64
#    0xca17ef    net/textproto.(*Reader).ReadLine+0xaf        D:/Program Files/Go/src/net/textproto/reader.go:44
#    0xca180a    net/http.readRequest+0xca            D:/Program Files/Go/src/net/http/request.go:1087
#    0xc934c6    net/http.(*conn).readRequest+0x226        D:/Program Files/Go/src/net/http/server.go:1048
#    0xc980f7    net/http.(*conn).serve+0x397            D:/Program Files/Go/src/net/http/server.go:2027

1 @ 0xab57f1 0xaf395d 0xcc15d1 0xcc1405 0xcbe22b 0xcd788f 0xcd833e 0xc99c29 0xc9bac4 0xca2b0e 0xc98385 0xafc6a1
#    0xcc15d0    runtime/pprof.writeRuntimeProfile+0xb0    D:/Program Files/Go/src/runtime/pprof/pprof.go:796
#    0xcc1404    runtime/pprof.writeGoroutine+0x44    D:/Program Files/Go/src/runtime/pprof/pprof.go:755
#    0xcbe22a    runtime/pprof.(*Profile).WriteTo+0x14a    D:/Program Files/Go/src/runtime/pprof/pprof.go:377
#    0xcd788e    net/http/pprof.handler.ServeHTTP+0x52e    D:/Program Files/Go/src/net/http/pprof/pprof.go:272
#    0xcd833d    net/http/pprof.Index+0xdd        D:/Program Files/Go/src/net/http/pprof/pprof.go:389
#    0xc99c28    net/http.HandlerFunc.ServeHTTP+0x28    D:/Program Files/Go/src/net/http/server.go:2294
#    0xc9bac3    net/http.(*ServeMux).ServeHTTP+0x1c3    D:/Program Files/Go/src/net/http/server.go:2822
#    0xca2b0d    net/http.serverHandler.ServeHTTP+0x8d    D:/Program Files/Go/src/net/http/server.go:3301
#    0xc98384    net/http.(*conn).serve+0x624        D:/Program Files/Go/src/net/http/server.go:2102

1 @ 0xaf4b0e 0xab8c77 0xaf3ce5 0xb47f47 0xb487e5 0xb4a7a5 0xb4aad6 0xbb4b8b 0xbc583b 0xbc4a70 0xc9ce2c 0xc9ca31 0xcdd1f7 0xcdd1cf 0xafc6a1
#    0xaf3ce4    internal/poll.runtime_pollWait+0x84    D:/Program Files/Go/src/runtime/netpoll.go:351
#    0xb47f46    internal/poll.(*pollDesc).wait+0x26    D:/Program Files/Go/src/internal/poll/fd_poll_runtime.go:84
#    0xb487e4    internal/poll.execIO+0x104        D:/Program Files/Go/src/internal/poll/fd_windows.go:177
#    0xb4a7a4    internal/poll.(*FD).acceptOne+0x64    D:/Program Files/Go/src/internal/poll/fd_windows.go:946
#    0xb4aad5    internal/poll.(*FD).Accept+0x1b5    D:/Program Files/Go/src/internal/poll/fd_windows.go:980
#    0xbb4b8a    net.(*netFD).accept+0x4a        D:/Program Files/Go/src/net/fd_windows.go:182
#    0xbc583a    net.(*TCPListener).accept+0x1a        D:/Program Files/Go/src/net/tcpsock_posix.go:159
#    0xbc4a6f    net.(*TCPListener).Accept+0x2f        D:/Program Files/Go/src/net/tcpsock.go:380
#    0xc9ce2b    net/http.(*Server).Serve+0x30b        D:/Program Files/Go/src/net/http/server.go:3424
#    0xc9ca30    net/http.(*Server).ListenAndServe+0x70    D:/Program Files/Go/src/net/http/server.go:3350
#    0xcdd1f6    net/http.ListenAndServe+0x36        D:/Program Files/Go/src/net/http/server.go:3659
#    0xcdd1ce    main.main.func1+0xe            D:/workspace/GolandProjects/demo/day2/main.go:13

1 @ 0xaf4b0e 0xad3cc6 0xcdd14b 0xac1f3d 0xafc6a1
#    0xcdd14a    main.main+0x2a        D:/workspace/GolandProjects/demo/day2/main.go:20
#    0xac1f3c    runtime.main+0x27c    D:/Program Files/Go/src/runtime/proc.go:283

1 @ 0xaf4b0e 0xaf89c7 0xcdd178 0xafc6a1
#    0xaf89c6    time.Sleep+0x166        D:/Program Files/Go/src/runtime/time.go:338
#    0xcdd177    main.leakedGoroutine+0x17    D:/workspace/GolandProjects/demo/day2/main.go:25

1 @ 0xafc6a1

下面我们逐一分析每个goroutine组:

第一组:2个goroutine
堆栈显示它们正在等待网络I/O(internal/poll.runtime_pollWait),具体是在读取HTTP请求。这是正常的HTTP服务器行为,处理传入的请求。

第二组:1个goroutine
这个goroutine正在处理/debug/pprof的请求,具体是在写goroutine profile。这是我们在访问pprof端点时产生的,是正常的。

第三组:1个goroutine
这个goroutine在等待接受新的TCP连接(net.(*TCPListener).Accept),这是HTTP服务器监听新连接的部分,是正常的。

第四组:1个goroutine
这是主goroutine,正在执行main函数,是正常的。

第五组:1个goroutine
这个goroutine正在执行time.Sleep,位于函数main.leakedGoroutine中。根据函数名,这很可能是一个泄漏的goroutine,因为它只是一个简单的sleep,而且没有退出机制。

第六组:1个goroutine
这个goroutine的堆栈只有0xafc6a1,这可能是系统goroutine,例如垃圾回收等,但具体信息不足。不过,它可能是一个空闲的goroutine,或者是运行时有特殊用途的goroutine。
```

下面我们逐一分析每个goroutine组:

第一组:2个goroutine
堆栈显示它们正在等待网络I/O(internal/poll.runtime_pollWait),具体是在读取HTTP请求。这是正常的HTTP服务器行为,处理传入的请求。

第二组:1个goroutine
这个goroutine正在处理/debug/pprof的请求,具体是在写goroutine profile。这是我们在访问pprof端点时产生的,是正常的。

第三组:1个goroutine
这个goroutine在等待接受新的TCP连接(net.(*TCPListener).Accept),这是HTTP服务器监听新连接的部分,是正常的。

第四组:1个goroutine
这是主goroutine,正在执行main函数,是正常的。

第五组:1个goroutine
这个goroutine正在执行time.Sleep,位于函数main.leakedGoroutine中。根据函数名,这很可能是一个泄漏的goroutine,因为它只是一个简单的sleep,而且没有退出机制。

第六组:1个goroutine
这个goroutine的堆栈只有0xafc6a1,这可能是系统goroutine,例如垃圾回收等,但具体信息不足。不过,它可能是一个空闲的goroutine,或者是运行时有特殊用途的goroutine。

本作品采用《CC 协议》,转载必须注明作者和本文链接
欢迎转载分享提意见,一起code
棋布
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!