Go语言中局部变量的逃逸分析(从汇编的角度)

Go语言中局部变量的逃逸分析(从汇编的角度)

正常情况下,局部变量是存储在栈中的,如果将局部变量的地址当作函数值返回,这势必会导致悬挂指针的错误,因为函数返回后,函数的栈帧就会被回收,返回的局部变量地址自然就访问不到了。但是Go语言会进行逃逸分析,编译器如果遇到这种情况,就会将该变量分配到堆中而不是栈中,这样函数返回后,返回的地址自然就可以访问到之前的局部变量。

源码

package main

func main() {
    n := escape()
    *n = 20
    print(*n)
}

//go:noinline
func escape() *int {
    n := 11
    return &n
}

本次使用的Go版本

jagitch@34c4dd4d4a3e:relations$ go version
go version go1.22.2 linux/amd64

编译

go build main.go

反编译

jagitch@34c4dd4d4a3e:go-asm$ go tool objdump -S -s "main.escape" main
TEXT main.escape(SB) /home/coder/workspace/own/jagitch-code/gitee/go-study/go-asm/main.go
func escape() *int {
  0x45d160              493b6610                CMPQ SP, 0x10(R14)
  0x45d164              7621                    JBE 0x45d187
  0x45d166              55                      PUSHQ BP
  0x45d167              4889e5                  MOVQ SP, BP
  0x45d16a              4883ec10                SUBQ $0x10, SP
        n := 11
  0x45d16e              488d050b5d0000          LEAQ 0x5d0b(IP), AX
  0x45d175              e8e6edfaff              CALL runtime.newobject(SB)
  0x45d17a              48c7000b000000          MOVQ $0xb, 0(AX)
        return &n
  0x45d181              4883c410                ADDQ $0x10, SP
  0x45d185              5d                      POPQ BP
  0x45d186              c3                      RET
func escape() *int {
  0x45d187              e874cdffff              CALL runtime.morestack_noctxt.abi0(SB)
  0x45d18c              ebd2                    JMP main.escape(SB)
Go反汇编代码解析
  1. CMPQ SP, 0x10(R14) 判断是否需要增长栈

  2. JBE 0x45d187 如果需要则跳转到0x45d187执行增长栈的逻辑

  3. PUSHQ BP 将BP压站

  4. MOVQ SP, BP 保存main.escape函数的栈帧基址

  5. SUBQ $0x10, SP 在栈上分配16个字节的空间

  6. LEAQ 0x5d0b(IP), AX 将堆上的一个地址保存到AX

  7. CALL runtime.newobject(SB) 根据AX中的地址,创建对象

  8. MOVQ $0xb, 0(AX) 将11保存到0(AX)中,即保存到刚刚创建的对象的地址处

  9. ADDQ $0x10, SP 清理栈帧

  10. POPQ BP 恢复调用者的函数栈帧基址

  11. RET 返回

  12. CALL runtime.morestack_noctxt.abi0(SB) 调用运行时中的函数去扩展栈空间

  13. JMP main.escape(SB)跳转到函数开始重新执行函数

结论

从反汇编的代码中可以看出,将局部变量的地址返回时,该局部变量会使用CALL runtime.newobject(SB)在堆中分配而不是分配在栈中,即使函数返回了,该指针指向的内存地址还是可以正常使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gopyer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值