[golang] サイズが未知の場合は、ヒープに配置されることを理解する。

# 目的
スライスが作成されるとき、容量が未知の場合は、ヒープに配置されることを理解する。

# 環境
```
$ go version
go version go1.23.2 linux/amd64
```

# 検証
## (1) スライスの容量が既知
スライスはヒープにエスケープされていない(スタックに配置されている)ことがわかる。
``` main.go
package main

func main() {
	_ = make([]byte, 1, 2)
}

```
``` main_test.go
package main

import "testing"

func Benchmark(b *testing.B) {
	for i := 0; i < b.N; i++ {
		main()
	}
}

```
```
$ go build -gcflags "-m=2" .
# gopractice
./main.go:3:6: can inline main with cost 5 as: func() { _ = make([]byte, 1, 2) }
./main.go:4:10: make([]byte, 1, 2) does not escape
```
```
$ go test -bench=. -benchmem
goos: linux
goarch: amd64
pkg: gopractice
cpu: Intel(R) Xeon(R) CPU E5-2686 v4 @ 2.30GHz
Benchmark-2     1000000000               0.3725 ns/op          0 B/op          0 allocs/op
PASS
ok      gopractice      0.418s
```
```
$ go build -o main_binary main.go
$ objdump -d main_binary > objdump.txt
0000000000466be0 <main.main>:
  466be0:	c3                   	ret
  466be1:	cc                   	int3
  466be2:	cc                   	int3
  466be3:	cc                   	int3
  466be4:	cc                   	int3
  466be5:	cc                   	int3
  466be6:	cc                   	int3
  466be7:	cc                   	int3
  466be8:	cc                   	int3
  466be9:	cc                   	int3
  466bea:	cc                   	int3
  466beb:	cc                   	int3
  466bec:	cc                   	int3
  466bed:	cc                   	int3
  466bee:	cc                   	int3
  466bef:	cc                   	int3
  466bf0:	cc                   	int3
  466bf1:	cc                   	int3
  466bf2:	cc                   	int3
  466bf3:	cc                   	int3
  466bf4:	cc                   	int3
  466bf5:	cc                   	int3
  466bf6:	cc                   	int3
  466bf7:	cc                   	int3
  466bf8:	cc                   	int3
  466bf9:	cc                   	int3
  466bfa:	cc                   	int3
  466bfb:	cc                   	int3
  466bfc:	cc                   	int3
  466bfd:	cc                   	int3
  466bfe:	cc                   	int3
  466bff:	cc                   	int3
```

## (2) スライスの容量が未知
スライスはヒープにエスケープされていることがわかる。
``` main.go
package main

func main() {
	n := 2
	_ = make([]byte, 1, n)
}

```
``` main_test.go
package main

import "testing"

func Benchmark(b *testing.B) {
	for i := 0; i < b.N; i++ {
		main()
	}
}

```
```
$ go build -gcflags "-m=2" .
# gopractice
./main.go:3:6: can inline main with cost 10 as: func() { n := 2; _ = make([]byte, 1, n) }
./main.go:5:10: make([]byte, 1, n) escapes to heap:
./main.go:5:10:   flow: {heap} = &{storage for make([]byte, 1, n)}:
./main.go:5:10:     from make([]byte, 1, n) (non-constant size) at ./main.go:5:10
./main.go:5:10: make([]byte, 1, n) escapes to heap
```
```
$ go test -bench=. -benchmem
goos: linux
goarch: amd64
pkg: gopractice
cpu: Intel(R) Xeon(R) CPU E5-2686 v4 @ 2.30GHz
Benchmark-2     88923357                12.78 ns/op            2 B/op          1 allocs/op
PASS
ok      gopractice      1.156s
```
```
$ go build -o main_binary main.go
$ objdump -d main_binary > objdump.txt
0000000000466be0 <main.main>:
  466be0:	49 3b 66 10          	cmp    0x10(%r14),%rsp
  466be4:	76 25                	jbe    466c0b <main.main+0x2b>
  466be6:	55                   	push   %rbp
  466be7:	48 89 e5             	mov    %rsp,%rbp
  466bea:	48 83 ec 18          	sub    $0x18,%rsp
  466bee:	48 8d 05 4b 56 00 00 	lea    0x564b(%rip),%rax        # 46c240 <type:*+0x5240>
  466bf5:	bb 01 00 00 00       	mov    $0x1,%ebx
  466bfa:	b9 02 00 00 00       	mov    $0x2,%ecx
  466bff:	90                   	nop
  466c00:	e8 5b 79 ff ff       	call   45e560 <runtime.makeslice>
  466c05:	48 83 c4 18          	add    $0x18,%rsp
  466c09:	5d                   	pop    %rbp
  466c0a:	c3                   	ret
  466c0b:	e8 10 b1 ff ff       	call   461d20 <runtime.morestack_noctxt.abi0>
  466c10:	eb ce                	jmp    466be0 <main.main>
  466c12:	cc                   	int3
  466c13:	cc                   	int3
  466c14:	cc                   	int3
  466c15:	cc                   	int3
  466c16:	cc                   	int3
  466c17:	cc                   	int3
  466c18:	cc                   	int3
  466c19:	cc                   	int3
  466c1a:	cc                   	int3
  466c1b:	cc                   	int3
  466c1c:	cc                   	int3
  466c1d:	cc                   	int3
  466c1e:	cc                   	int3
  466c1f:	cc                   	int3
```

## (3)
スライスの容量が既知でも、そのスタックフレーム以外に引き渡されるならば、ヒープにエスケープされる。
`2 allocs/op`を見ると、二回のヒープへのエスケープが発生している。`fmt.Printf("%p\n", &s)` は、スライス構造体のアドレスを引き渡しており、これにより、スライス構造体のエスケープと、スライス要素のエスケープが発生するので、二回のエスケープが発生している。

``` main.go
package main

import "fmt"

func main() {
	s := make([]byte, 1, 17)
	fmt.Printf("%p\n", &s)
	fmt.Printf("%p\n", &s[0])
}

```
``` main_test.go
package main

import "testing"

func Benchmark(b *testing.B) {
	for i := 0; i < b.N; i++ {
		main()
	}
}

```
```
$ go build -gcflags "-m=2" .
# gopractice
./main.go:5:6: cannot inline main: function too complex: cost 171 exceeds budget 80
./main.go:7:12: inlining call to fmt.Printf
./main.go:8:12: inlining call to fmt.Printf
./main.go:6:11: make([]byte, 1, 17) escapes to heap:
./main.go:6:11:   flow: s = &{storage for make([]byte, 1, 17)}:
./main.go:6:11:     from make([]byte, 1, 17) (spill) at ./main.go:6:11
./main.go:6:11:     from s := make([]byte, 1, 17) (assign) at ./main.go:6:4
./main.go:6:11:   flow: {storage for ... argument} = s:
./main.go:6:11:     from s[0] (dot of pointer) at ./main.go:8:23
./main.go:6:11:     from &s[0] (address-of) at ./main.go:8:21
./main.go:6:11:     from &s[0] (interface-converted) at ./main.go:8:21
./main.go:6:11:     from ... argument (slice-literal-element) at ./main.go:8:12
./main.go:6:11:   flow: fmt.a = &{storage for ... argument}:
./main.go:6:11:     from ... argument (spill) at ./main.go:8:12
./main.go:6:11:     from fmt.format, fmt.a := "%p\n", ... argument (assign-pair) at ./main.go:8:12
./main.go:6:11:   flow: {heap} = *fmt.a:
./main.go:6:11:     from fmt.Fprintf(os.Stdout, fmt.format, fmt.a...) (call parameter) at ./main.go:8:12
./main.go:6:2: s escapes to heap:
./main.go:6:2:   flow: {storage for ... argument} = &s:
./main.go:6:2:     from &s (address-of) at ./main.go:7:21
./main.go:6:2:     from &s (interface-converted) at ./main.go:7:21
./main.go:6:2:     from ... argument (slice-literal-element) at ./main.go:7:12
./main.go:6:2:   flow: fmt.a = &{storage for ... argument}:
./main.go:6:2:     from ... argument (spill) at ./main.go:7:12
./main.go:6:2:     from fmt.format, fmt.a := "%p\n", ... argument (assign-pair) at ./main.go:7:12
./main.go:6:2:   flow: {heap} = *fmt.a:
./main.go:6:2:     from fmt.Fprintf(os.Stdout, fmt.format, fmt.a...) (call parameter) at ./main.go:7:12
./main.go:6:2: moved to heap: s
./main.go:6:11: make([]byte, 1, 17) escapes to heap
./main.go:7:12: ... argument does not escape
./main.go:8:12: ... argument does not escape
```
```
$ go test -bench=. -benchmem
...
0xc000250d08
0xc00024ef18
0xc000250d20
0xc00024ef30
0xc000250d38
0xc00024ef48
   62806             18745 ns/op              48 B/op          2 allocs/op
PASS
ok      gopractice      1.376s
```
```
$ go build -o main_binary main.go
$ objdump -d main_binary > objdump.txt
0000000000490880 <main.main>:
  490880:	49 3b 66 10          	cmp    0x10(%r14),%rsp
  490884:	0f 86 fc 00 00 00    	jbe    490986 <main.main+0x106>
  49088a:	55                   	push   %rbp
  49088b:	48 89 e5             	mov    %rsp,%rbp
  49088e:	48 83 ec 60          	sub    $0x60,%rsp
  490892:	48 8d 05 27 80 00 00 	lea    0x8027(%rip),%rax        # 4988c0 <type:*+0x78c0>
  490899:	e8 e2 bf f7 ff       	call   40c880 <runtime.newobject>
  49089e:	48 89 44 24 58       	mov    %rax,0x58(%rsp)
  4908a3:	bb 01 00 00 00       	mov    $0x1,%ebx
  4908a8:	b9 11 00 00 00       	mov    $0x11,%ecx
  4908ad:	48 8d 05 cc 8e 00 00 	lea    0x8ecc(%rip),%rax        # 499780 <type:*+0x8780>
  4908b4:	e8 c7 5a fd ff       	call   466380 <runtime.makeslice>
  4908b9:	48 8b 4c 24 58       	mov    0x58(%rsp),%rcx
  4908be:	48 c7 41 08 01 00 00 	movq   $0x1,0x8(%rcx)
  4908c5:	00 
  4908c6:	48 c7 41 10 11 00 00 	movq   $0x11,0x10(%rcx)
  4908cd:	00 
  4908ce:	83 3d fb 3c 0e 00 00 	cmpl   $0x0,0xe3cfb(%rip)        # 5745d0 <runtime.writeBarrier>
  4908d5:	74 0f                	je     4908e6 <main.main+0x66>
  4908d7:	e8 84 b0 fd ff       	call   46b960 <runtime.gcWriteBarrier2>
  4908dc:	49 89 03             	mov    %rax,(%r11)
  4908df:	48 8b 11             	mov    (%rcx),%rdx
  4908e2:	49 89 53 08          	mov    %rdx,0x8(%r11)
  4908e6:	48 89 01             	mov    %rax,(%rcx)
  4908e9:	48 8d 15 10 7c 00 00 	lea    0x7c10(%rip),%rdx        # 498500 <type:*+0x7500>
  4908f0:	48 89 54 24 48       	mov    %rdx,0x48(%rsp)
  4908f5:	48 89 4c 24 50       	mov    %rcx,0x50(%rsp)
  4908fa:	48 8b 1d 67 3b 0c 00 	mov    0xc3b67(%rip),%rbx        # 554468 <os.Stdout>
  490901:	48 8d 05 f0 4f 04 00 	lea    0x44ff0(%rip),%rax        # 4d58f8 <go:itab.*os.File,io.Writer>
  490908:	bf 03 00 00 00       	mov    $0x3,%edi
  49090d:	48 8d 74 24 48       	lea    0x48(%rsp),%rsi
  490912:	41 b8 01 00 00 00    	mov    $0x1,%r8d
  490918:	4d 89 c1             	mov    %r8,%r9
  49091b:	48 8d 0d 82 f6 01 00 	lea    0x1f682(%rip),%rcx        # 4affa4 <go:string.*+0x4c>
  490922:	e8 19 98 ff ff       	call   48a140 <fmt.Fprintf>
  490927:	48 8b 54 24 58       	mov    0x58(%rsp),%rdx
  49092c:	48 83 7a 08 00       	cmpq   $0x0,0x8(%rdx)
  490931:	76 47                	jbe    49097a <main.main+0xfa>
  490933:	48 8b 12             	mov    (%rdx),%rdx
  490936:	4c 8d 15 43 64 00 00 	lea    0x6443(%rip),%r10        # 496d80 <type:*+0x5d80>
  49093d:	4c 89 54 24 38       	mov    %r10,0x38(%rsp)
  490942:	48 89 54 24 40       	mov    %rdx,0x40(%rsp)
  490947:	48 8b 1d 1a 3b 0c 00 	mov    0xc3b1a(%rip),%rbx        # 554468 <os.Stdout>
  49094e:	48 8d 05 a3 4f 04 00 	lea    0x44fa3(%rip),%rax        # 4d58f8 <go:itab.*os.File,io.Writer>
  490955:	48 8d 0d 48 f6 01 00 	lea    0x1f648(%rip),%rcx        # 4affa4 <go:string.*+0x4c>
  49095c:	bf 03 00 00 00       	mov    $0x3,%edi
  490961:	48 8d 74 24 38       	lea    0x38(%rsp),%rsi
  490966:	41 b8 01 00 00 00    	mov    $0x1,%r8d
  49096c:	4d 89 c1             	mov    %r8,%r9
  49096f:	e8 cc 97 ff ff       	call   48a140 <fmt.Fprintf>
  490974:	48 83 c4 60          	add    $0x60,%rsp
  490978:	5d                   	pop    %rbp
  490979:	c3                   	ret
  49097a:	31 c0                	xor    %eax,%eax
  49097c:	48 89 c1             	mov    %rax,%rcx
  49097f:	90                   	nop
  490980:	e8 7b b3 fd ff       	call   46bd00 <runtime.panicIndex>
  490985:	90                   	nop
  490986:	e8 55 92 fd ff       	call   469be0 <runtime.morestack_noctxt.abi0>
  49098b:	e9 f0 fe ff ff       	jmp    490880 <main.main>
```
```
$ go run main.go
0xc00009e000
0xc0000a0000
```

 # 参考

 https://www.amazon.co.jp/Go%E8%A8%80%E8%AA%9E-100Tips-%E3%81%82%E3%82%8A%E3%81%8C%E3%81%A1%E3%81%AA%E3%83%9F%E3%82%B9%E3%82%92%E6%8A%8A%E6%8F%A1%E3%81%97%E3%80%81%E5%AE%9F%E8%A3%85%E3%82%92%E6%9C%80%E9%81%A9%E5%8C%96%E3%81%99%E3%82%8B-impress-gear/dp/4295017531
 > 例えば、`s := make(int[], 10)`はヒープにエスケープされないかもしれませんが、`s := make([]int, n)`はそのサイズが変数に基づいているのでヒープにエスケープされます。