🐥

【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で確認

  1. ターミナルでサンプルコード実行

    1. go run main.go
  2. 別ターミナルで下記コマンドの実行

    1. go tool pprof http://localhost:6060/debug/pprof/heap
  3. topコマンド実行でメモリ食ってる関数を一覧表示

    1. ここで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
    
  4. 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:		// 少し待つ
    
  5. web コマンドでブラウザ上でグラフで視覚化

    1. 使用するにはGraphvizが必要(Macならbrew install graphvizでインストール)
GitHubで編集を提案

Discussion