【Go笔记】函数与切片
使用
- 函数是一类公民
- 可以作为变量,参数传递、返回、以及赋值
- 多返回值,返回结果和错误
闭包与陷阱
¶闭包
- 包含函数的入口地址和其关联的环境
- 闭包可以引用闭包外的变量
¶陷阱
1 |
|
- 这个程序里面,不同的协程获取的由于是
val
的内存地址,而range会使得val地址上的值不断刷新,因此输出的不一定是遍历。 - 可以改为闭包函数传参,从而解决这个问题
1 |
|
函数栈
- 遵循LIFO(后进先出)的规则,增长方向是从高地址到低地址
- 函数执行时,参数,返回地址,局部变量会被压入,退出时弹出
- 每个命令执行的时候使用内存的一片区域,为栈帧
- 调用函数时,栈帧从高到低的结构为
BP --> 传参 --> 返回值 --> 保存返回值的变量 --> 返回地址
- 函数栈只保存返回地址,参数以及必要外围信息,函数本身代码保存在代码区中,和栈帧分开
栈扩容和栈转移
¶栈扩容
- 每个协程都有一个栈,初始化为2KB
- 64位系统的栈最大为1GB,32位为250MB
- 在序言阶段判断是否需要对栈进行扩容,通过比较
寄存器rsp
和stackguard0
的大小,若rsp小,那就有扩容的需要 - 扩容时,新栈的大小是老栈的两倍,然后再将数据从老栈转移到新栈上面
¶栈转移
- 首先设定状态为
_Gcopystack
,让垃圾回收不要扫描 - 分配新栈的内存,在Linux下,对于2/4/8/16KB的小栈会有专门的优化,在每个逻辑处理器中预先分配缓冲池,回收的时候回到缓冲池中
- 对于大栈来说,如果缓冲池中没有对应大小的栈,那就从堆中去拿
- 分配到新栈之后,如果有指针指向旧栈,那么就要调整到新栈去,涉及到内存复制
栈调试
- 可以从修改Go的源码进行源码层面的栈调试,如修改
stackDebug
为1来调试 - 也可以使用
pprof
标准库来调试,获取CPU瓶颈之类的信息
【Go笔记】函数与切片
https://study.0x535a.cn/go-note/go-func-stack/