【Go】わざとメモリリークするサンプルコードと検出方法
更新日:2025/5/3/(土) 03:59
タグ:Go
概要
- Go言語を使っていてメモリリークを経験したことがないので、意図的にメモリリークを発生させるサンプルコードを書いてみた
- メモリリークしているかも下記を使用して確認してみた
pprof
(Goの標準profiler)go tool pprof
コマンドでメモリの使用状況を可視化
サンプルコード
package main import ( "fmt" "net/http" _ "net/http/pprof" "time" ) // グローバル変数として巨大なスライスへの参照を保持 var leakedData = make([][]byte, 0) func memoryLeak() { for { // 1MBのデータを作成 data := make([]byte, 1024*1024) // グローバル変数にデータの参照を保持し続ける leakedData = append(leakedData, data) // 少し待つ time.Sleep(100 * time.Millisecond) } } func main() { // pprof用にHTTPサーバを起動(:6060でアクセス可能) go func() { fmt.Println(http.ListenAndServe("localhost:6060", nil)) }() // メモリリークを発生させる関数を呼び出し memoryLeak() }
サンプルコード解説
net/http/pprof
をimportすることで、http://localhost:6060/debug/pprof/
にアクセスできるようになるmemoryLeak()
関数内で1MBのデータを生成し続け、参照をleakedData
に保持する- 本来なら使い終わったら開放されるべきだが、グローバル変数に参照が残ることでGCの対象にならずメモリを消費し続ける
pprofで確認
-
ターミナルでサンプルコード実行
go run main.go
-
別ターミナルで下記コマンドの実行
go tool pprof http://localhost:6060/debug/pprof/heap
-
top
コマンド実行でメモリ食ってる関数を一覧表示- ここで8.1MB全部が memoryLeak 関数内で確保され、解放されずに生き残ってるのがわかる
(pprof) top Showing nodes accounting for 8.10MB, 100% of 8.10MB total flat flat% sum% cum cum% 8.10MB 100% 100% 8.10MB 100% main.memoryLeak (inline) 0 0% 100% 8.10MB 100% main.main 0 0% 100% 8.10MB 100% runtime.main
-
list 関数名
コマンドで行単位の詳細確認(pprof) list main.memoryLeak Total: 8.10MB ROUTINE ======================== main.memoryLeak in /Users/isurugikeito/dev/go/go-demo/demo/low_layer/main.go 8.10MB 8.10MB (flat, cum) 100% of Total . . 13:func memoryLeak() { . . 14: for { . . 15: // 1MBのデータを作成 8.10MB 8.10MB 16: data := make([]byte, 1024*1024) . . 17: . . 18: // グローバル変数にデータの参照を保持し続ける . . 19: leakedData = append(leakedData, data) . . 20: . . 21: // 少し待つ
-
web
コマンドでブラウザ上でグラフで視覚化- 使用するにはGraphvizが必要(Macなら
brew install graphviz
でインストール)
- 使用するにはGraphvizが必要(Macなら