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

时间:2017-12-14 16:37:07

标签: 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)
        }
    }
}

如果这属于Code Review,那么我会把它放在那里。

1 个答案:

答案 0 :(得分:6)

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

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,网络搜索会发出更多关于该主题的文章。