在讲Go的堆栈之前,先温习一下堆栈基础知识。
什么是堆栈?在计算机中堆栈的概念分为:数据结构的堆栈和内存分配中堆栈。
数据结构的堆栈:
堆:堆可以被看成是一棵树,如:堆排序。在队列中,调度程序反复提取队列中第一个作业并运行,因为实际情况中某些时间较短的任务将等待很长时间才能结束,或者某些不短小,但具有重要性的作业,同样应当具有优先权。堆即为解决此类问题设计的一种数据结构。
栈:一种先进后出的数据结构。
这里着重讲的是内存分配中的堆和栈。
内存分配中的堆和栈
栈(操作系统):由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
堆(操作系统): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。
堆栈缓存方式
栈使用的是一级缓存, 他们通常都是被调用时处于存储空间中,调用完毕立即释放。
堆则是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些。
堆栈跟踪
下面讨论堆栈跟踪信息以及如何在堆栈中识别函数所传递的参数。
以下测试案例的版本是Go 1.11
示例:
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()
}
列表1是一个简单的程序, main函数在第5行调用Example函数。Example函数在第9行声明,它有三个参数,一个字符串slice,一个字符串和一个整数。它的方法体也很简单,只有一行,debug.PrintStack(),这会立即产生一个堆栈跟踪信息:
goroutine 1 [running]:
runtime/debug.Stack(0x1, 0x0, 0x0)
C:/Go/src/runtime/debug/stack.go:24 +0xae
runtime/debug.PrintStack()
C:/Go/src/runtime/debug/stack.go:16 +0x29
main.Example(0xc000077f48, 0x2, 0x4, 0x4abd9e, 0x5, 0xa)
D:/gopath/src/example/example/main.go:10 +0x27
main.main()
D:/gopath/src/example/example/main.go:7 +0x79
堆栈跟踪信息:
第一行显示运行的goroutine是id为 1的goroutine。
第二行 debug.Stack()被调用
第四行 debug.PrintStack() 被调用
第六行 调用debug.PrintStack()的代码位置,位于main package下的Example函数。它也显示了代码所在的文件和路径,以及debug.PrintStack()发生的行数(第10行)。
第八行 也调用Example的函数的名字,它是main package的main函数。它也显示了文件名和路径,以及调用Example函数的行数。
下面主要分析 传递Example函数传参信息
// Declaration
main.Example(slice []string, str string, i int)
// Call to Example by main.
slice := make([]string, 2, 4)
Example(slice, "hello", 10)
// Stack trace
main.Example(0x2080c3f50, 0x2, 0x4, 0x425c0, 0x5, 0xa)
上面列举了Example函数的声明,调用以及传递给它的值的信息。当你比较函数的声明以及传递的值时,发现它们并不一