go - 为什么这个 Golang 应用程序运行的时间越长,使用的内存就越多?

标签 go memory-leaks

我做这个是为了监控一些网站,并在其中一个网站出现故障时通知我。我只在两个网址上测试它。当它启动时,它使用大约 5mb 的内存(我用 systemctl status monitor 检查过)。 40 分钟后,它使用了 7.4mb。 8 小时后,它使用了超过 50mb 的内存。为什么要这样做?这叫内存泄漏吗?

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
    "os"
    "sync"
    "time"

    "monitor/utils/slack"

    "gopkg.in/yaml.v2"
)

var config struct {
    Frequency int
    Urls      []string
}

type statusType struct {
    values map[string]int
    mux    sync.Mutex
}

var status = statusType{values: make(map[string]int)}

func (s *statusType) set(url string, value int) {
    s.mux.Lock()
    s.values[url] = value
    s.mux.Unlock()
}

func init() {
    data, err := ioutil.ReadFile("config.yaml")
    if err != nil {
        fmt.Printf("Invalid config: %s\n", err)
        os.Exit(0)
    }

    err = yaml.Unmarshal(data, &config)
    if err != nil {
        fmt.Printf("Invalid config: %s\n", err)
        os.Exit(0)
    }

    for _, url := range config.Urls {
        status.set(url, 200)
    }
}

func main() {
    ticker := time.NewTicker(time.Duration(config.Frequency) * time.Second)
    for _ = range ticker.C {
        for _, url := range config.Urls {
            go check(url)
        }
    }
}

func check(url string) {
    res, err := http.Get(url)
    if err != nil {
        res = &http.Response{StatusCode: 500}
    }

    // the memory problem occurs when this condition is never satisfied, so I didn't post the slack package.
    if res.StatusCode != status.values[url] {
        status.set(url, res.StatusCode)

        err := slack.Alert(url, res.StatusCode)
        if err != nil {
            fmt.Println(err)
        }
    }
}

如果这属于代码审查,那么我会把它放在那里。

最佳答案

是的,这是内存泄漏。我可以发现的一个明显来源是您没有关闭请求的响应主体:

func check(url string) {
    res, err := http.Get(url)
    if err != nil {
        res = &http.Response{StatusCode: 500}
    } else {
        defer res.Body.Close()    // You need to close the response body!
    }

    if res.StatusCode != status.values[url] {
        status.set(url, res.StatusCode)

        err := slack.Alert(url, res.StatusCode)
        if err != nil {
            fmt.Println(err)
        }
    }
}

更好的是,为了让 Go 可以使用 keepalive,你想要读取完整的主体并关闭它:

defer func() {
    io.Copy(ioutil.Discard, res.Body)
    res.Body.Close()
}()

您可以通过使用 pprof 分析您的应用程序来进一步分析内存使用的来源。 .有 a good rundown on the Go blog网络搜索会找到更多关于该主题的文章。

关于go - 为什么这个 Golang 应用程序运行的时间越长,使用的内存就越多?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47818191/

相关文章:

mongodb - mongodb无法在Go中执行事务,并且总是得到无法在多文档事务中创建 namespace

Go Profiler (pprof) 时间差异

azure - 从 k8s 解析 yaml 配置映射数据

go - 传递结构

java - 帮助 Eclipse 内存分析器 [MAT]

go - 如何在查询路由器中使用多个参数

c++ - 关于 Safe 类是异常安全的声明的澄清

c++ - 程序终止后动态分配的内存

java - 拍照时数据库泄漏。 SQLiteDatabase 创建且从未关闭。安卓、Java、SQLite

java - 将大型 hprof 加载到 jhat