后端 - Go defer 原理和源码剖析 - 个人文章 - SegmentFault 思否 - https://segmentfault.com/a/1190000040950441
The empty struct
defer
type
form basic or not:
basic:
- int, int8,int32
- float32,float34
- string
composite type:
- container:
- array
- map
- slice
- pointer
- channel;
- struct;
- interface
- function
value type and reference type.
value type:
- store and pass: 值本身
- 对子字段(元素)修改不会影响原值
reference type:
- store and pass: 值的引用(pointer)
value type:
- basic type
- Numeric types:
int
, int8
, int16
, int32
, int64
uint
, uint8
, uint16
, uint32
, uint64
, uintptr
float32
, float64
complex64
, complex128
- Boolean type:
bool
- String type:
string
- Array type:
[]T
- Struct type:
struct{}
reference type:
- Slice type:
[]T
- Map type:
map[K]V
- Channel type:
chan T
- Function type:
func(params) returnType
pointer type
pass by value
pass by value: 传递的是变量的值
pass by reference: 传递的是变量的引用
go is pass by value:
- 对于值类型, 传递的是值本身: value
- 对于引用类型, 传递的是引用值, reference value
type convert
type assertion: 获取 infefac 的真实类型
1
2
|
var a interface{} = 100
value,ok := a.(int)
|
defer
what: a function that last call
use case:
- close resource: channle, file, context
feature:
- stack
- 立即传递参数
1
2
3
4
5
6
7
8
9
10
|
func hello() {
for _, v := range []int{1, 2, 3, 4} {
v := v
defer func() {
fmt.Println(v)
}()
}
fmt.Println("hello")
}
// hello, 4, 3,2,1
|
how
how:
- create defer: deferproc
- excute defer: deferreturn
1
2
|
// 内存布局
[ defer struct ][arg1,arg2,arg3.... ]
|
structure: in goroutinue.deferlinklist
1
2
3
4
5
6
7
8
9
10
11
|
type _defer struct {
siz int32 // 参数和返回值的内存大小
started bool
heap bool // 区分该结构是在栈上分配的,还是对上分配的
sp uintptr // sp 计数器值,栈指针;
pc uintptr // pc 计数器值,程序计数器;
fn *funcval // defer 传入的函数地址,也就是延后执行的函数;
_panic *_panic // panic that is running defer
link *_defer // 链表
}
|
create defer:
- copy parameter
- push in go.defer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
func deferproc(siz int32, fn *funcval) { // arguments of fn follow fn
sp := getcallersp(unsafe.Pointer(&siz))
argp := uintptr(unsafe.Pointer(&fn)) + unsafe.Sizeof(fn)
callerpc := getcallerpc() // 存储的是 caller 中,call deferproc 的下一条指令的地址
d := newdefer(siz)
if d._panic != nil {
throw("deferproc: d.panic != nil after newdefer")
}
d.fn = fn
d.pc = callerpc
d.sp = sp
switch siz {
case 0:
// Do nothing.
case sys.PtrSize:
*(*uintptr)(deferArgs(d)) = *(*uintptr)(unsafe.Pointer(argp))
default:
memmove(deferArgs(d), unsafe.Pointer(argp), uintptr(siz))
}
return0()
// No code can go here - the C return register has
// been set and must not be clobbered.
}
func newdefer(siz int32) *_defer {
var d *_defer
sc := deferclass(uintptr(siz))
gp := getg()
if sc < uintptr(len(p{}.deferpool)) {
// 从 p 结构体的 deferpool 中获取可用的 defer struct
// 代码比较简单,省略
}
if d == nil {
// 上面没有成功获取到可用的 defer struct
// 因此需要切换到 g0 生成新的 defer struct
systemstack(func() {
total := roundupsize(totaldefersize(uintptr(siz)))
d = (*_defer)(mallocgc(total, deferType, true))
})
}
// defer func 的参数大小
d.siz = siz
// 链表链接
// 后 defer 的在前,类似一个栈结构
d.link = gp._defer
// 修改当前 g 的 defer 结构体,指向新的 defer struct
gp._defer = d
return d
}
|
excute defer:
- check g in current func.
- excute defer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
func deferreturn(arg0 uintptr) {
gp := getg()
// 获取到最前的 _defer 节点
d := gp._defer
// 函数递归终止条件(d 链表遍历完成)
if d == nil {
return
}
// 获取 caller 函数的 rsp 寄存器值
sp := getcallersp()
if d.sp != sp {
// 如果 _defer.sp 和 caller 的 sp 值不一致,那么直接返回;
// 因为,就说明这个 _defer 结构不是在该 caller 函数注册的
return
}
switch d.siz {
case 0:
// Do nothing.
case sys.PtrSize:
*(*uintptr)(unsafe.Pointer(&arg0)) = *(*uintptr)(deferArgs(d))
default:
memmove(unsafe.Pointer(&arg0), deferArgs(d), uintptr(d.siz))
}
// 获取到延迟回调函数地址
fn := d.fn
d.fn = nil
// 把当前 _defer 节点从链表中摘除
gp._defer = d.link
// 释放 _defer 内存(主要是堆上才会需要处理,栈上的随着函数执行完,栈收缩就回收了)
freedefer(d)
// 执行延迟回调函数
jmpdefer(fn, uintptr(unsafe.Pointer(&arg0)))
}
|
explain why
case: stack
1
2
3
4
5
6
7
|
func main() {
for i := 0; i < 10; i++ {
defer fmt.Println(i)
}
fmt.Println("done")
}
// output: done, 9...1
|
case 2: 立即拷贝参数
1
2
3
4
5
6
|
func a(){
var c = 0
defer fmt.Println(c)
c = 100;
}
// output:0; copy value instantly
|
case3: f1 修改的的是本地变量, f2直接修改返回值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
func f1 () (r int) {
t := 1
defer func() {
t = t + 5
}()
return t
}
func f3() (r int) {
defer func () {
r = r + 5
} ()
return 1
}
|
make&&new
make:
- 申请内存,初始化, value = initialized value
- 返回引用
- used for: map, slice, chan
new:
- 申请内存,未初始化,value= zero value
- 返回指针
- used for all types
make:
1
2
3
4
5
|
make(map[int]int):
make(chan int)
make([]int,5,10)
|
empty struct/array
what: zero field
feature:
- zero size
- may in some address
1
2
3
4
5
6
7
8
9
|
func main() {
var s1, s2 struct{}
fmt.Println(unsafe.Sizeof(s1), unsafe.Sizeof(s2), unsafe.Pointer(&s1), unsafe.Pointer(&s2))
}
// 0 0 0x116d0d0 0x116d0d0
|
use case: 不需要实际变量的场景下, 作为一个占位符, 优化内存
- channel 同步信号
- 作为mthod(不需要更新状态)的实现类型
- map 实现 set
1
2
3
4
5
6
7
8
|
func addtoSets(a ...int) map[int]struct{} {
m := make(map[int]struct{})
for _, v := range a {
m[v] = struct{}{}
}
return m
}
|
zero value
what: 一个特殊的value, 如果没有被初始化,则会被赋予的值
zero value for following kind:
- slice
- map
- channel
- pointer
- interface
- fucntion
features:
- different size for different type:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
package main
import (
"fmt"
"unsafe"
)
func main() {
var p *struct{} = nil
fmt.Println( unsafe.Sizeof( p ) ) // 8
var s []int = nil
fmt.Println( unsafe.Sizeof( s ) ) // 24
var m map[int]bool = nil
fmt.Println( unsafe.Sizeof( m ) ) // 8
var c chan string = nil
fmt.Println( unsafe.Sizeof( c ) ) // 8
var f func() = nil
fmt.Println( unsafe.Sizeof( f ) ) // 8
var i interface{} = nil
fmt.Println( unsafe.Sizeof( i ) ) // 16
}
|
- not equal for different types:
1
2
|
(interface{})(nil) == (*int)(nil) // fasle
(*int)(nil) == (*bool)(nil) // compile error
|
comparable
what:
是否可以使用 ==;
comparable,==
- primitive type:same value
- pointer: same address
- channel: same reference
1
2
|
var a = make(chan int)
b := a
|
- struct: if child filed are comparable
- interface: if actual type are comparable
uncomparable
不能使用 ==(除了 nil),只能 reflect.DeepEqual
- function: == nil;
- map: key and value are equal;
- slice: index and value are equal;
two nil alway == ?
nil 的类型一致才相等
1
2
3
4
5
|
var p *int = nil
var i interface{} = nil
if p == i {
fmt.Println("Equal")
}
|
pass by value
reference vs pointer:
reference: safer pointer, auto address and dereference
pass by value: 传递(复制)的变量的值
pass by reference: 传递变量的地址
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
func add(num int){
num += 1
}
var a = 100
add(a)
fmt.Println(a)
func addRef(num &int){
num+=1
}
var a = 100
addRef(a)
fmt.Printlin(a)//101
//
int* num = &a
*num+=1
|