KDOC 415: io.Readerのシグネチャはヒープエスケープを避ける設計になっている

この文書のステータス

  • 作成
    • 2025-07-01 貴島
  • レビュー
    • <署名>

概要

io.Reader, io.Writerのインターフェース設計には、ヒープのエスケープも関係しているという。

type Reader interface {
        Read(p []byte) (n int, err error)
}

[]byte の初期化は呼び出し側の責務になっている。これによって呼び出し側がメモリ管理できるようになる。スタックに留まり、スタックにエスケープするのを抑えられる。Readを何度呼んでも新しいヒープ確保が発生しない。

実際に試してみる。

tmpfile=$(mktemp /tmp/tmpgo.XXXXXX.go)

cat > $tmpfile <<EOF
package main

func main() {
  // スタックに置いたまま
  buf := make([]byte, 2)
  Read2(buf)
}

// Read側で初期化して返すと、ヒープにエスケープしなければならない
func Read1() []byte {
  return make([]byte, 1)
}

// 呼び出し側で初期化してもらうようにすると、ヒープにエスケープする必要がない
func Read2([]byte) {}

EOF

go build -gcflags="-m" -o bin $tmpfile 2>&1
rm bin
rm $tmpfile
# command-line-arguments
/tmp/tmpgo.Q5eDch.go:12:6: can inline Read2
/tmp/tmpgo.Q5eDch.go:3:6: can inline main
/tmp/tmpgo.Q5eDch.go:8:6: can inline Read1
/tmp/tmpgo.Q5eDch.go:5:8: inlining call to Read2
/tmp/tmpgo.Q5eDch.go:4:14: make([]byte, 2) does not escape
/tmp/tmpgo.Q5eDch.go:9:14: make([]byte, 1) escapes to heap

関連