Goroutines内存泄漏

时间:2018-04-28 22:57:58

标签: go memory-leaks channel goroutine

UPD:重构代码,没有任何变化

我在这个功能中有内存泄漏,但我不知道在哪里。

func CheckProxySOCKS(prox string, c chan QR) (err error) {

    //Sending request through proxy
    dialer, _ := proxy.SOCKS5("tcp", prox, nil, proxy.Direct)
    timeout := time.Duration(5 * time.Second)
    httpClient := &http.Client{Timeout: timeout, Transport: &http.Transport{Dial: dialer.Dial}}
    res, err := httpClient.Get("https://api.ipify.org?format=json")

    if err != nil {

        c <- QR{Addr: prox, Res: false}
        return
    }

    _, err = ioutil.ReadAll(res.Body)
    res.Body.Close()
    if err != nil {
        return
    }

    c <- QR{Addr: prox, Res: true}
    return
}

我在这里称之为

for _, proxy := range splitedProxies {
    go code.CheckProxySOCKS(proxy, respChan)
}

for range splitedProxies {
    r := <-respChan
    if r.Res {
        checkedProxiesArray = append(checkedProxiesArray, r.Addr)
    }
}

在3-4个周期后,我得到了超过40k的goroutines(我会通过runtime.NumGoroutine()检查)。启动应用程序后,使用大约100mb后4个循环超过1GB

所有代码

Github回购

2 个答案:

答案 0 :(得分:1)

你的问题在于没有关闭响应主体,在这一行:

_, err := httpClient.Get("https://api.ipify.org?format=json")

获取响应变量并按顺序关闭主体,如下所示:

r, err := httpClient.Get("https://api.ipify.org?format=json")
if err != nil {
    c <- QR{Addr: prox, Res: false}
    return
}
defer r.Body.Close()

c <- QR{Addr: prox, Res: true}

在文档The client must close the response body when finished with it: here

答案 1 :(得分:1)

您并不总是关闭连接,并且您正在使用新的客户端传输每个请求。如果丢弃传输,则会丢弃空闲池中的任何连接,从而泄漏这些资源。

来自http.Transport docs

  

默认情况下,Transport会缓存连接以供将来重复使用。访问许多主机时,这可能会留下许多开放的连接。可以使用Transport的CloseIdleConnections方法和MaxIdleConnsPerHost和DisableKeepAlives字段来管理此行为。

     

应该重复使用运输,而不是根据需要创建。运输对于多个goroutine同时使用是安全的。

在没有错误时始终关闭响应正文,并始终重复使用传输。