【问题标题】:Allocated a lot of memory in Go. How to fix?在 Go 中分配了大量内存。怎么修?
【发布时间】:2019-06-10 02:40:35
【问题描述】:

为 50 个 5 MB 的请求分配了数百 MB 的内存。内存已分配,不再释放。 我怎样才能清除我的记忆?为什么会发生这种情况?

我已经在我的家用电脑和 VPS 上尝试过 Ubuntu

package main

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

func main() {
    fmt.Println("start")

    for i := 0; i < 50; i++ {
        go func() {
            DoRequest()
        }()
        time.Sleep(10 * time.Millisecond)
    }

    time.Sleep(10 * time.Minute)
}

func DoRequest() error {
    requestUrl := "https://blockchain.info/rawblock/0000000000000000000eebedea046425bd54626e6c56eb032e66e714d0141ea6"

    req, err := http.NewRequest("GET", requestUrl, nil)

    if err != nil {
        return err
    }

    req.Header.Set("user-agent", "free")

    httpClient := &http.Client{
        Timeout: time.Second * 10,
    }

    resp, err := httpClient.Do(req)

    if resp != nil {
        defer resp.Body.Close()
    }

    if err != nil {
        return err
    }

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return err
    }

    fmt.Println("bodylen", len(body))

    return nil
}

分配到某处 400MB

【问题讨论】:

  • 分配是正常的。保持它直到系统上有足够的内存压力供操作系统回收它是正常的。你有什么问题?

标签: go memory memory-leaks


【解决方案1】:

您正在为每个 go-routine 创建一个 http 客户端。

Http 客户端被设计为一次创建并多次使用。它们是常规安全的。 它们允许连接重用和其他提高效率的方法。

在 main 中创建一次 http 客户端(而不是在您的 go-routines 中),然后将这个单一引用传递给您的所有 50 个 go-routines。


编辑:此外,虽然它可能对您的情况没有实际影响,但请求的顺序通常是这样的:

resp, err := httpClient.Do(req)
if err != nil {
        return err // check error first
}
defer resp.Body.Close() // no error - so resp will *NOT* be nil - so this is safe

编辑 2: 正如@Adrian 所提到的:go 的垃圾收集不是即时的——也不应该是即时的——因为这是一项昂贵的操作。如果您不再需要一块内存 - 只需不再引用它。让 GC 完成它的工作,这样您就可以专注于自己的工作!

如果你对go的GC演变感到好奇:

【讨论】:

  • 没有帮助。感谢回复
  • 每个服务器容器有 256MB 内存。而且我需要同时处理许多响应大小很大的请求。现在内存不会消失,它会冻结并且不会被释放。所有的内存都被填满了,不能再释放了。连 512MB 都不够,检查一下
  • 您是否修正了上述错误检查的顺序?如果响应主体由于错误而不是有效响应,则关闭resp != nil 可能会导致不可预知的结果。
【解决方案2】:
 for i := 0; i < 50; i++ {
        go func() {
            DoRequest()
        }()
        time.Sleep(10 * time.Millisecond)
    }

永远不要创建这样的 go-routines。始终确保您创建 go-routines 的方式在任何情况下(包括最坏的情况)都不会填满大(全部)内存

简单的解决方案是控制每次可以生成(或运行)的 go-routines 的数量。

您可以通过乘以一次要运行的 go-routines 的最大数量来预先计算在最坏情况下占用的内存,并且 max-memory 可以被一个 go-routine 使用。

您可以使用通道来控制 go-routines 的实例。

请参阅此 stackoverflow 问题的第一个答案 Always have x number of goroutines running at any time

始终在性能和所需资源之间使用平衡的解决方案。


2019 年 6 月 11 日更新

这里是示例 go 程序 https://play.golang.org/p/HovNRgp6FxH

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-02-19
    • 2016-09-12
    • 2012-04-20
    • 2011-08-09
    • 2018-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多