栈内存不足逃逸到堆内存,但是到底达到多大的内存时才会发生逃逸呢?

结论:当栈内单个对象大小超过64KB,则会发生内存逃逸

栈内存不足逃逸到堆内存的场景分析如下:

type student struct {
    name string // 16byte
}

func stackSpace() {
    // 动态大小,发生逃逸
    length := 10
    space1 := make([]int, length)
    for i := 0; i < len(space1); i++ {
        space1[i] = i
    }
    len1 := unsafe.Sizeof(space1)
    fmt.Println("space1 length:", len1)

    // 未发生逃逸
    space2 := make([]int, 100, 100)
    for i := 0; i < len(space2); i++ {
        space2[i] = i
    }
    len2 := unsafe.Sizeof(space2)
    fmt.Println("space2 length:", len2)

    // 当int32整型数组容量大于8192*2时(64KB,单个int32大小为4字节),发生逃逸
    space3 := make([]int32, 8192*2+1, 8192*2+1)
    for i := 0; i < len(space3); i++ {
        space3[i] = int32(i)
    }
    len3 := unsafe.Sizeof(space3)
    fmt.Println("space3 length:", len3)

    // 当int64整型数组容量大于8192时(64KB,单个int64大小为8字节),发生逃逸
    space4 := make([]int64, 8193, 8193)
    for i := 0; i < len(space4); i++ {
        space4[i] = int64(i)
    }
    len4 := unsafe.Sizeof(space4)
    fmt.Println("space4 length:", len4)

    // 当字符串指针数组容量大于4096时(64KB,单个字符串大小为16字节),发生逃逸
    space5 := make([]string, 4097, 4097)
    for i := 0; i < len(space5); i++ {
        s := "a"
        space5[i] = s
    }
    len5 := unsafe.Sizeof(space5)
    fmt.Println("space5 length:", len5)

    // 当字符串指针数组容量大于8192时(64KB,单个指针大小为8字节),发生逃逸,变量s也发生逃逸
    space6 := make([]*string, 8193, 8193)
    for i := 0; i < len(space6); i++ {
        s := "abc"
        space6[i] = &s
    }
    len6 := unsafe.Sizeof(space6)
    fmt.Println("space6 length:", len6)

    // 当自定义对象数组容量大于4096时(64KB,单个对象大小为16字节),发生逃逸
    space7 := make([]student, 4097, 4097)
    for i := 0; i < len(space7); i++ {
        space7[i].name = "abc"
    }
    len7 := unsafe.Sizeof(space7)
    fmt.Println("space7 length:", len7)
}

对单个文件进行逃逸分析:

go build -gcflags='-m -l' memory_analysis.go

逃逸分析结果,可以直接看make语句日志

# command-line-arguments
./memory_analysis.go:67:9: moved to heap: s
./memory_analysis.go:24:19: make([]int, length) escapes to heap
./memory_analysis.go:29:16: ... argument does not escape
./memory_analysis.go:29:17: "space1 length:" escapes to heap
./memory_analysis.go:29:17: len1 escapes to heap
./memory_analysis.go:32:19: make([]int, 100, 100) does not escape
./memory_analysis.go:37:16: ... argument does not escape
./memory_analysis.go:37:17: "space2 length:" escapes to heap
./memory_analysis.go:37:17: len2 escapes to heap
./memory_analysis.go:40:19: make([]int32, 8192 * 2 + 1, 8192 * 2 + 1) escapes to heap
./memory_analysis.go:45:16: ... argument does not escape
./memory_analysis.go:45:17: "space3 length:" escapes to heap
./memory_analysis.go:45:17: len3 escapes to heap
./memory_analysis.go:48:19: make([]int64, 8193, 8193) escapes to heap
./memory_analysis.go:53:16: ... argument does not escape
./memory_analysis.go:53:17: "space4 length:" escapes to heap
./memory_analysis.go:53:17: len4 escapes to heap
./memory_analysis.go:56:19: make([]string, 4097, 4097) escapes to heap
./memory_analysis.go:62:16: ... argument does not escape
./memory_analysis.go:62:17: "space5 length:" escapes to heap
./memory_analysis.go:62:17: len5 escapes to heap
./memory_analysis.go:65:19: make([]*string, 8193, 8193) escapes to heap
./memory_analysis.go:71:16: ... argument does not escape
./memory_analysis.go:71:17: "space6 length:" escapes to heap
./memory_analysis.go:71:17: len6 escapes to heap
./memory_analysis.go:74:19: make([]student, 4097, 4097) escapes to heap
./memory_analysis.go:79:16: ... argument does not escape
./memory_analysis.go:79:17: "space7 length:" escapes to heap
./memory_analysis.go:79:17: len7 escapes to heap