2014-01-12 42 views
15

一展身手的二進制實現的HTTP服務器: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來阻止Keep-Alive。似乎沒有任何工作。

我最初在試圖確定我正在編寫的程序中是否存在內存泄漏時發現了這個問題。它有很多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" 
+0

它是緩存數據,還是收集日誌,調試信息和診斷? – Rygu

+0

這不是真的做了很多。我點擊/將打開一個文件(ioutil.ReadAll),通過文本/模板運行並輸出它。在該頁面上沒有任何SQL調用,但一切都關閉了。此時沒有記錄數據/日誌/信息/診斷。我刪除了我能想到的所有東西。 – Mark

+0

您使用的是哪個版本的Go? –

回答

18

pprof你的意見提供了堆,它看起來像你通過gorilla/sessionsgorilla/context(幾乎400MB)內存泄漏。

請參閱本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") 
} 
+0

是的,它看起來像CookieStore泄漏。當我將其剝離時,內存使用情況相當正常。我不明白爲什麼我需要'context.ClearHandler'。至於準系統HTTP服務器上的內存使用量上升,這似乎只是垃圾收集器。 – Mark

+4

'gorilla/context'在內部將數據存儲在'map [request] ...'(和'sessions'使用'context')中,因此處理程序需要在請求終止後從地圖中刪除請求。 它看起來像'大猩猩/會話'被設計成與'大猩猩/多路複用器'路由器一起使用(它自動清除地圖)。 – Alex

+0

謝謝,我檢查了網站,沒有提到這一點。 – Mark