Go http标准库中的内存泄漏?

问题描述

pprof您在注释中提供的堆中,看来您正在通过gorilla/sessionsgorilla/context(将近400MB)泄漏内存。

请参阅以下ML线程:[https](https://groups.google.com/forum/#!msg/gorilla-
web/clJfCzenuWY/N_Xj9-5Lk6wJ) :
[//groups.google.com/forum/#!](https://groups.google.com/forum/#!msg/gorilla-
web/clJfCzenuWY/N_Xj9-5Lk6wJ) msg/gorilla-web/clJfCzenuWY/
[N_Xj9-5Lk6wJ](https://groups.google.com/forum/#!msg/gorilla-
web/clJfCzenuWY/N_Xj9-5Lk6wJ)和此GH问题:https
//github.com/gorilla/sessions/issues/ 15

这是一个泄漏速度非常快的版本:

package main

import (
    "fmt"
    // "github.com/gorilla/context"
    "github.com/gorilla/sessions"
    "net/http"
)

var (
    cookieStore = sessions.NewCookieStore([]byte("cookie-secret"))
)

func main() {
    http.HandleFunc("/", defaultHandler)
    http.ListenAndServe(":8080", nil)
}

func defaultHandler(w http.ResponseWriter, r *http.Request) {
    cookieStore.Get(r, "leak-test")
    fmt.Fprint(w, "Hi there")
}

这是一个清理并具有相对静态的RSS的示例:

package main

import (
    "fmt"
    "github.com/gorilla/context"
    "github.com/gorilla/sessions"
    "net/http"
)

var (
    cookieStore = sessions.NewCookieStore([]byte("cookie-secret"))
)

func main() {
    http.HandleFunc("/", defaultHandler)
    http.ListenAndServe(":8080", context.ClearHandler(http.DefaultServeMux))
}

func defaultHandler(w http.ResponseWriter, r *http.Request) {
    cookieStore.Get(r, "leak-test")
    fmt.Fprint(w, "Hi there")
}

解决方法

让Go二进制程序实现http服务器:

package main

import (
    "net/http"
)

func main() {
    http.ListenAndServe(":8080",nil)
}

它将以大约850 kb的内存开始。通过网络浏览器发送一些请求。观察它很快上升到1 mb。如果您等待,您将看到它永远不会失败。现在,使用Apache
Bench对其进行锤打(使用下面的脚本),并查看您的内存使用量在不断增加。一段时间后,它将最终稳定在8.2 MB左右。

编辑:它似乎并没有在8.2处停止,而是明显放慢了速度。目前为9.2,并且仍在上升。

简而言之,为什么会这样?我使用了这个shell脚本:

while [ true ]
do
    ab -n 1000 -c 100 http://127.0.0.1:8080/
    sleep 1
end

在尝试深入了解这一点的同时,我尝试调整设置。我尝试关闭使用r.Close = true以防止保持活动状态。似乎没有任何作用。

我最初是在尝试确定正在编写的程序中是否存在内存泄漏时才发现的。它有很多http处理程序和I /
O调用。检查完所有数据库连接后,我继续看到它的内存使用量增加。我的程序稳定在 433 MB 左右。

这是Goenv的输出:

GOARCH="amd64"
GOBIN=""
GOCHAR="6"
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/Users/mark/Documents/Programming/Go"
GORACE=""
GOROOT="/usr/local/go"
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
TERM="dumb"
CC="clang"
GOGCCFLAGS="-g -O2 -fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fno-common"
CXX="clang++"
CGO_ENABLED="1"