golangで書かれたプログラムのメモリ使用状況を見る

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ごとのシンボルテーブルっぽく見えるけどあんまり読み方がわかってない。リロードするとその時々のメモリ状況が見れて面白い。

f:id:hakobe932:20140410010531p:plain

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

意図的にメモリリークしてみたら、けっこうたくさんリークしててやばい。