はじめに
Goのスライスの挙動について調べた。
容量を超える要素を追加したときは、元の容量を2倍したスライスが作られる。
元のスライスをコピーして新しいスライスが作られるため、メモリ上のアドレスが異なる。
つまり、元のスライスの分だけ、余分にメモリを消費するということ。
package main
import "fmt"
func main() {
s1 := make([]int, 1, 5)
fmt.Println("Len:", len(s1), "Cap:", cap(s1)) // Len: 1 Cap: 5
fmt.Printf("%p\n", s1) // 0xc00001a150
// 容量を超える要素を追加
s1 = append(s1, 1)
s1 = append(s1, 2)
s1 = append(s1, 3)
s1 = append(s1, 4)
s1 = append(s1, 5)
fmt.Println("Len:", len(s1), "Cap:", cap(s1)) // Len: 6 Cap: 10
fmt.Printf("%p\n", s1) // 0xc00001e050
}
ということは、下記のような場合、スライスのコピーが何度も繰り返され、メモリを消費する。
package main
import "fmt"
func main() {
s1 := make([]int, 0)
for i := 0; i < 10000; i++ {
s1 = append(s1, i)
fmt.Println("Len:", len(s1), "Cap:", cap(s1))
fmt.Printf("%p\n", s1)
}
}
なので、処理をする前に既にスライスの大きさが分かっているなら、長さか容量を指定してスライスを初期化しましょう。
package main
import "fmt"
func main() {
s1 := make([]int, 0, 10000)
for i := 0; i < 10000; i++ {
s1 = append(s1, i)
fmt.Println("Len:", len(s1), "Cap:", cap(s1))
fmt.Printf("%p\n", s1)
}
}