Ошибка утечки памяти в HTTP-библиотеке Go?
У вас есть двоичный код Go для запуска http-сервера:
package main
import (
"net/http"
)
func main() {
http.ListenAndServe(":8080", nil)
}
Он начнется с ~ 850 kb или около того памяти. Отправьте несколько запросов через ваш веб-браузер. Наблюдайте за ним, быстро поднимаясь до 1 мб. Если вы подождете, вы увидите, что он никогда не опускается. Теперь забейте его скамьей Apache (используя script ниже) и постоянно увеличивайте использование памяти. Через некоторое время это будет плато около 8,2 МБ или около того.
Изменить: он, похоже, не останавливается на отметке 8.2, а значительно замедляется. В настоящее время он составляет 9,2 и продолжает расти.
Короче говоря, почему это происходит? Я использовал эту оболочку script:
while [ true ]
do
ab -n 1000 -c 100 http://127.0.0.1:8080/
sleep 1
end
Пытаясь разобраться в этом, я попытался настроить настройки. Я попытался закрыть с помощью r.Close = true
, чтобы предотвратить Keep-Alive. Кажется, что ничего не работает.
Я нашел это изначально, пытаясь определить, была ли утечка памяти в программе, которую я пишу. В нем много обработчиков HTTP и вызовов ввода-вывода. После проверки я закрыл все мои подключения к базе данных, и я продолжал наблюдать за ростом использования памяти. Моя программа на плато около 433 МБ.
Здесь вывод 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"
Ответы
Ответ 1
Из кучи pprof
, который вы указали в комментариях, похоже, что вы пропускаете память через gorilla/sessions
и gorilla/context
(почти 400 МБ).
Обратитесь к этому потоку ML: 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")
}