Go语言编译声明“//go”

284 阅读2分钟

缘起

在写单元测试的过程中遇到了函数内敛的情况,当时并不准备在全局禁止内敛的使用,在查询资料的过程发现可以使用//go:noinline禁用内敛。在解决问题问题之后,对//go使用场景比较好奇,在这里总结一下。

简单来说,//go的作用就是告诉编译器,接下来要做什么指令,注意//go之间是没有空格。

具体类型

//go:noinline

noinline的作用是:禁止函数内敛

关于内敛可以看文章Go语言内敛机制

//go:nosplit

nosplit的作用是:跳过栈溢出检测

我们知道创建一个goroutine的成本非常小,初始占用的内存只有2k,随着函数运行,可能会动态增大变量内存,Go的运行时会检查分配的内存是不是快要用完了,并且在用完之前适当增加内存,这里说的split就是干这个的。

如果声明了//go:nosplit可以提高性能,但是可能出现stack overflow而导致编译失败。

//go:noescape

noescape 的作用是:禁止逃逸,而且它必须指示一个只有声明没有主体的函数

逃逸简单来说就是分配在栈上的内存转移到了堆中,如果禁止逃逸,那么函数执行完成之后,变量就会被销毁,可能会出现空指针的情况。

//go:norace

norace 的作用是:跳过竞态检测

如果多个协程会同时写临界区,就会出现静态竞争的情况。

举个例子,

var sum int

func main() {
    go add()
    go add()
}

func add() {
    sum++
}

执行go run -race main.go利用-race来使编译器报告数据竞争问题。

WARNING: DATA RACE
Read at 0x00c000140078 by goroutine 8:
  main.main.func1()
      /Users/p/Desktop/code/other/go-demo/main.go:63 +0x46

Previous write at 0x00c000140078 by goroutine 7:
  main.main.func1()
      /Users/p/Desktop/code/other/go-demo/main.go:63 +0x5c

Goroutine 8 (running) created at:
  main.main()
      /Users/p/Desktop/code/other/go-demo/main.go:66 +0x4d4

Goroutine 7 (finished) created at:
  main.main()
      /Users/p/Desktop/code/other/go-demo/main.go:65 +0x4be
==================
Found 1 data race(s)

经验分享:

曾经线上环境出了goroutine空指针问题,由于代码较为复杂,花了很长的时间都没有能够找到问题的原因,如果使用//go:norace或许能够简单定位问题。

参考资料