golangにはpprof用のプロファイルデータを出力できるライブラリが標準でついてくるので、それらを使うことでメモリの使用状況を調べることができる。中でも、net/http/pprofが手軽で便利だった。
net/http/pprofをプログラムに組み込むことでダイナミックなプロファイル情報をWebブラウザで表示してみることができる。使い方は、ライブラリの解説ページにあるとおりなんだけど、プロファイルを取りたいプログラムで
import _ "net/http/pprof"
とimportしたあと、main関数などで
go func() { log.Println(http.ListenAndServe("localhost:6060", nil)) }()
と書いておくと良い。
この状態でプログラムをbuildして実行する。プログラムの実行中に、http://localhost:6060/debug/pprof/heap?debug=1 にアクセスすると、プログラムのメモリの使用状況が表示されるので、下の方にあるruntime.MemStatsを調べたりしましょう。runtime.MemStats以外の部分は、goroutineごとのシンボルテーブルっぽく見えるけどあんまり読み方がわかってない。リロードするとその時々のメモリ状況が見れて面白い。
pprofコマンドの入力にURLがそのまま使えるので、以下のように調査したりもできる。pprofコマンドを使うといろんなビジュアライズができるらしい。コールグラフをsvgで出力したりもできそう。
$ go tool pprof --text http://localhost:6060/debug/pprof/heap | head -n 10 Read http://localhost:6060/debug/pprof/symbol Fetching /pprof/heap profile from localhost:6060 to /var/folders/xr/w4h_3ccx43dg7xjc9xj14m_00000gn/T/1KRgavcr87 Wrote profile to /var/folders/xr/w4h_3ccx43dg7xjc9xj14m_00000gn/T/1KRgavcr87 Adjusting heap profiles for 1-in-524288 sampling rate Total: 453.1 MB 431.6 95.3% 95.3% 431.6 95.3% bytes.makeSlice 5.5 1.2% 96.5% 5.5 1.2% bufio.NewReaderSize 5.5 1.2% 97.7% 5.5 1.2% main.func·002 3.5 0.8% 98.5% 3.5 0.8% bufio.NewWriter 2.5 0.6% 99.0% 2.5 0.6% newdefer 1.5 0.3% 99.3% 11.0 2.4% net/http.(*Transport).dialConn 1.0 0.2% 99.6% 3.5 0.8% net/http.(*persistConn).readLoop 0.5 0.1% 99.7% 0.5 0.1% net.sockaddrToTCP 0.5 0.1% 99.8% 0.5 0.1% net/http.ReadResponse
意図的にメモリリークしてみたら、けっこうたくさんリークしててやばい。