1.常量是被精确表示的并且只在编译时期可以被计算
参考:
Methods, Interfaces and Embedded Types in Go
比如可以定义:
// Much larger value than int64.
const (
myConst = 9223372036854775808543522345
myConst2 = myConst / 10000000000000
)
那么 myConst 不能赋值给变量,因为超过了 int64 范围,而myConst2则可以。
2.切片 slice: [i,j,k]
参考:
Ardan Labs Blog Three-Index Slices in Go 1.2 Three-Index Slices in Go 1.2
k-i可以限定新切片的 cap 大小
3.当切片遍历频率高时不一定要使用指针类型数据存放到切片或数组
参考:
Using Pointers In Go
比如:
var bs []BuoyStation
可以让cpu缓存接下来的连续数据,如果使用指针,则只是指针的值在内存中连续,而数据值则是分散的。
4.逃逸分析相关
参考:
Language Mechanics On Escape Analysis
Language Mechanics On Memory Profiling
1) 返回指针逃逸
//go:noinline
func createUserV1() user {
u := user{
name: "Bill",
email: "bill@ardanlabs.com",
}
println("V1", &u)
return u
}
//go:noinline
func createUserV2() *user {
u := user{
name: "Bill",
email: "bill@ardanlabs.com",
}
println("V2", &u)
return &u
}
createUserV1 会使 u 被分配在栈上,且有两份副本(调用 createUserV1 的栈帧和 createUserV1 内部的栈帧)
而 createUserV2 的 u 会被分配到堆上,由 GC 管理。
2) interface{} 逃逸
使用 interface{} 作为函数参数也会使得逃逸,
input := &Buffer{buf: buf}
...
io.ReadFull(input, buf[:end])
比如 io.ReadFull 使用了 Read 接口,也会产生逃逸。
input := &Buffer{buf: buf}
...
input.Read(buf[:end])
改为 input.Read(buf[:end]) 则不存在 interface{} 转换,从而不存在逃逸。
至于为什么传入 interface 会被逃逸,这有个issue https://github.com/golang/go/issues/7213
以及到现在 go1.9 时还存在的编译器 BUG
Escape-Analysis Flaws
其中在当前 1.11 时以下 BUG 已经被解决了:
var y2 int
func(p *int, x int) {
*p = x
}(&y2, 42) // BAD: Cause of y2 escape, 1.11 is GOOD
3) 对于编译期内存大小不确定的变量也会逃逸
buf := make([]byte, size)
比如对于 make 中传入运行时才知道的变量 size 则会使得编译期无法知道 buf 大小,从而产生逃逸。
改成固定大小后就会被分配在栈上了(前提不能超过最大大小限制)
buf := make([]byte, 5)