597 字
3 分钟
Go 语言学习笔记 -第18章

Go 语言学习笔记 -第18章#

堆空间&栈空间#

在讲Go的堆栈之前,先温习一下堆栈基础知识。

什么是堆栈?在计算机中堆栈的概念分为:数据结构的堆栈和内存分配中堆栈。

堆栈跟踪#

使用的go 版本是go 1.13.1

例子:

package main
import "runtime/debug"
func main() {
slice := make([]string, 2, 4)
Example(slice, "hello", 10)
}
func Example(slice []string, str string, i int) {
debug.PrintStack()
}

运行结果:

goroutine 1 [running]:
runtime/debug.Stack(0xc000041f50, 0xc000099f50, 0x404dd6)
c:/go/src/runtime/debug/stack.go:24 +0xa4
runtime/debug.PrintStack()
c:/go/src/runtime/debug/stack.go:16 +0x29
main.Example(...)
d:/学习/golang/example/main.go:10
main.main()
d:/学习/golang/example/main.go:7 +0x28
Process exiting with code: 0

堆栈跟踪信息: 第一行显示运行的goroutine是id为 1的goroutine。

第二行 debug.Stack()被调用

第四行 debug.PrintStack() 被调用

第六行 调用debug.PrintStack()的代码位置,位于main package下的Example函数。它也显示了代码所在的文件和路径,以及debug.PrintStack()发生的行数(第10行)。

第八行 也调用Example的函数的名字,它是main package的main函数。它也显示了文件名和路径,以及调用Example函数的行数。

变量判断#

变量是堆(heap)还是堆栈(stack)?

在当前的编译器中,如果变量具有其地址,则该变量是堆上分配的候选变量。 但是,基础的逃逸分析可以将那些生存不超过函数返回值的变量识别出来,并且因此可以分配在栈上。

Go的编译器会决定在哪(堆or栈)分配内存,保证程序的正确性。

下面通过反汇编查看具体内存分配情况:

新建 main.go

package main
import "fmt"
func main() {
var a [1]int
c := a[:]
fmt.Println(c)
}

查看汇编代码

go tool compile -S main.go

输出:

...
0x0028 00040 (main.go:5) FUNCDATA $2, gclocals·bfec7e55b3f043d1941c093912808913(SB)
0x0028 00040 (main.go:5) FUNCDATA $3, "".main.stkobj(SB)
0x0028 00040 (main.go:6) PCDATA $0, $1
0x0028 00040 (main.go:6) PCDATA $1, $0
0x0028 00040 (main.go:6) LEAQ type.[1]int(SB), AX
0x002f 00047 (main.go:6) PCDATA $0, $0
0x002f 00047 (main.go:6) MOVQ AX, (SP)
0x0033 00051 (main.go:6) CALL runtime.newobject(SB)
0x0038 00056 (main.go:6) PCDATA $0, $1
...

注意到有调用newobject!其中main.go:6说明变量a的内存是在堆上分配的!

修改main.go

package main
func main() {
var a [1]int
c := a[:]
println(c)
}

再看汇编代码:

没有发现调用newobject,这段代码a是在堆栈上分配的

结论:

Go 编译器自行决定变量分配在堆栈或堆上,以保证程序的正确性。

Go 语言学习笔记 -第18章
https://blog.zhequtao.com/posts/golang/go-learning/学习笔记-go/第18章/
作者
阿俊
发布于
2020-03-30
许可协议
CC BY-NC-SA 4.0

相关阅读