【问题标题】:Memory leak from client.Do()来自 client.Do() 的内存泄漏
【发布时间】:2021-08-22 10:18:51
【问题描述】:

getAPIData 函数用于获取 HTML 字符串,需要每 5 秒调用一次。我使用runtime.ReadMemStats 函数检查了内存使用情况。

如下面的输出所示,内存缓慢上升,但从未下降。

Alloc = 1 MiB   TotalAlloc = 2 MiB      Sys = 71 MiB    NumGC = 1
...
Alloc = 3 MiB   TotalAlloc = 4 MiB      Sys = 71 MiB    NumGC = 1
...
Alloc = 3 MiB   TotalAlloc = 6 MiB      Sys = 71 MiB    NumGC = 2
Alloc = 2 MiB   TotalAlloc = 7 MiB      Sys = 71 MiB    NumGC = 3
...
Alloc = 3 MiB   TotalAlloc = 9 MiB      Sys = 71 MiB    NumGC = 3
...
Alloc = 3 MiB   TotalAlloc = 10 MiB     Sys = 71 MiB    NumGC = 4

我为我的问题制作了示例代码。我应该怎么做才能修复那个内存泄漏?

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
    "runtime"
    "time"
)

func getAPIData() (string, error) {
    req, err := http.NewRequest("GET", "https://google.com/", nil)
    if err != nil {
        //fmt.Println(err.Error())
        return "", err
        //panic(err)
    }
    req.Header.Add("User-Agent", "Mozilla/5.0")

    client := &http.Client{Timeout: 3 * time.Second}
    resp, err := client.Do(req) // cuase of memory leak
    if err != nil {
        fmt.Println(err.Error())
        return "", err
        //panic(err)
    }
    defer resp.Body.Close()

    bytes, err2 := ioutil.ReadAll(resp.Body)
    if err2 != nil {
        fmt.Println(err2.Error())
        return "", err2
        //panic(err)
    }
    return string(bytes), nil
}

func PrintMemUsage() {
    var m runtime.MemStats
    runtime.ReadMemStats(&m)

    fmt.Printf("Alloc = %v MiB", bToMb(m.Alloc))
    fmt.Printf("\tTotalAlloc = %v MiB", bToMb(m.TotalAlloc))
    fmt.Printf("\tSys = %v MiB", bToMb(m.Sys))
    fmt.Printf("\tNumGC = %v\n", m.NumGC)
}

func bToMb(b uint64) uint64 {
    return b / 1024 / 1024
}
func main() {
    for range time.Tick(5 * time.Second) {
        _, _ = getAPIData()
        PrintMemUsage()
    }
}

【问题讨论】:

  • 这没有显示任何泄漏。 TotalAlloc 是已分配的金额;如果释放一些内存,然后再次分配,则“分配的总量”会增加。随着每个分配的对象被释放,释放的计数也随之增加; Alloc 值减小,但 TotalAlloc 值不减小。
  • 由于您的 Alloc 值稳定在 3 MiB 左右,因此很明显没有实际泄漏。如果 TotalAlloc 为 10 且 Alloc 为 3,则必须沿途释放 7。
  • 了解 totalalloc 值。谢谢..

标签: go memory-leaks


【解决方案1】:

可能不是内存泄漏的来源,但在处理http.Clients 时docs 建议:

客户端和传输是安全的,可供多人同时使用 为了提高效率,goroutines 应该只创建一次并重复使用

因此,在您的示例中,更新您的 API 签名,以传入客户端:

func getAPIData(client *http.Client) (string, error)

并将(单个)客户端创建移动到main

func main() {

    cli := &http.Client{Timeout: 3 * time.Second} // create once ...

    for range time.Tick(5 * time.Second) {
        _, _ = getAPIData(cli)   // ... reuse many times
        PrintMemUsage()
    }
}

【讨论】:

    猜你喜欢
    • 2012-08-21
    • 1970-01-01
    • 1970-01-01
    • 2011-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多