You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Goroutines内存泄漏:重构代码后仍存在,无法定位泄漏点

排查Go语言CheckProxySOCKS函数的内存泄漏问题

嘿,我来帮你捋捋这个SOCKS代理检查函数里可能藏着的内存泄漏点。先看你贴的代码片段,有几个Go里常见的“内存泄漏坑”,咱们一个个说:

1. 未关闭HTTP响应体(最常见的疏漏)

你代码里调用httpClient.Get之后,不管请求成功还是失败,都没处理res.Body的关闭!在Go里,HTTP响应体必须手动关闭——哪怕你完全不需要读取响应内容,否则会导致底层的TCP连接被长期占用,内存没法释放。

修复起来很简单,用defer确保不管代码走哪个分支,响应体都会被关闭:

res, err := httpClient.Get("https://api.ipify.org?format=json")
if err != nil {
    c <- QR{Addr: prox, Res: ...} // 补全你的业务逻辑
    return err
}
// 关键!用defer延迟关闭响应体,放在err判断之后避免nil panic
defer res.Body.Close()

// 额外提醒:如果不需要响应内容,最好也把Body读完(比如用io.Copy到废弃流)
// 否则部分HTTP/1.1的连接可能不会被复用,也会占用资源
_, _ = io.Copy(io.Discard, res.Body)

2. 频繁创建HTTP客户端和传输器

你每次调用CheckProxySOCKS都新建一个http.Clienthttp.Transport,而Transport内部维护了一个连接池。如果这个函数被高频调用,大量闲置的连接池会堆积在内存里,没法被GC回收。

解决思路是复用资源

  • 如果代理地址是固定的,可以把DialerTransporthttp.Client做成全局变量,初始化一次反复使用;
  • 如果代理地址是动态变化的(每个调用用不同的prox),那用完Transport后要主动关闭闲置连接,减少资源占用:
func CheckProxySOCKS(prox string, c chan QR) (err error) {
    dialer, err := proxy.SOCKS5("tcp", prox, nil, proxy.Direct)
    if err != nil {
        c <- QR{Addr: prox, Res: ...}
        return err
    }

    transport := &http.Transport{Dial: dialer.Dial}
    client := &http.Client{Timeout: 5 * time.Second, Transport: transport}
    
    res, err := client.Get("https://api.ipify.org?format=json")
    // 用defer组合关闭操作
    defer func() {
        if res != nil {
            res.Body.Close()
        }
        // 关闭Transport的闲置连接,释放资源
        transport.CloseIdleConnections()
    }()

    if err != nil {
        c <- QR{Addr: prox, Res: ...}
        return err
    }

    // 处理响应逻辑...
    c <- QR{Addr: prox, Res: ...}
    return nil
}

3. 用pprof精准定位泄漏点

如果上面的修复还没解决问题,那得用Go的pprof工具抓一下内存快照,看看具体是哪个对象在内存里堆积:

  1. 在你的程序里引入pprof包:
import _ "net/http/pprof"
  1. 启动一个pprof的HTTP服务(放在main函数里):
go func() {
    log.Println(http.ListenAndServe("localhost:6060", nil))
}()
  1. 运行程序,然后在终端执行:
go tool pprof http://localhost:6060/debug/pprof/heap
  1. 进入pprof交互界面后,用top看内存占用最高的对象,用list CheckProxySOCKS查看函数里的内存分配细节,就能精准找到泄漏的位置了。

内容的提问来源于stack exchange,提问作者H.Denison

火山引擎 最新活动