Mastering Go 内部运行机制—垃圾回收?defer?
Chapter2
认识Go程序运行的内部机制
本章概览
介绍垃圾回收以及其运行机制、与C代码相互调用、panic、defer、recover
-
编译器
-
垃圾回收工作原理、以及如何检测垃圾回收情况
-
在Go中调用C,C中调用Go
-
panic、recover
-
unsafe包
-
defer
-
Linux工具strace、FreeBSD工具dtrace
-
Go环境信息
-
节点树
-
Go汇编
编译器
Go编译器需要在go tool的帮助下执行
-
编译
结果是一个包含目标代码的二进制文件,但是不能直接运行,只是目标代码的""直译"
Mastering Go apple$ go tool compile go_tool.go
Mastering Go apple$ ls
go_tool.go go_tool.o
Mastering Go apple$ go tool compile -pack go_tool.go
Mastering Go apple$ ls
go_tool.a go_tool.go go_tool.o
go tool compile -race go_tool.go
go tool compile -S go_tool.go
2. 打包
Mastering Go apple$ go tool compile -pack go_tool.go
Mastering Go apple$ ls
go_tool.a go_tool.go go_tool.o
在并发编程,可能涉及资源竞争、锁等问题,需要检测竞态条件
go tool compile -race go_tool.go
go tool compile -S go_tool.go
垃圾回收
更多信息查看runtime下mgc.go文件
-
目标
检测哪些对象超出范围并且不会被引用到,然后释放该空间2. 特点
2. 特点
-
-
写屏障
-
三色标记-清除算法
Go的垃圾回收器基于三色标记清除算法
三色标记清除算法 + 写屏障
垃圾回收器与Go的调度器并发执行
首要原则:把堆中的对象按照颜色分到不同的集合,颜色根据算法标记
三色集合
-
黑色集合
黑色集合中对象确保没有任何指针指向白色集合中对象
-
白色集合
白色集合中允许有指针指向黑色集合对象, 不会对垃圾回收造成影响;白色集合中对象就是垃圾回收的对象
-
灰色集合
灰色集合可能会有指针指向白色集合对象
注意:没有黑色对象能进入白色集合,这允许算法去操作并清除白色集合对象、并且黑色对象不能直接指向白色集合中的对象
回收流程
-
-
然后垃圾回收器遍历所以根对象,将其标记为灰色
根对象就是程序能直接访问的对象,全局变量以及栈里面
-
选取一个灰色对象,将其标记为黑色,然后去寻找该对象是否有指针指向白色集合的对象
-
灰色对象被其他对象所指向,那么灰色对象就会被标记为黑色,并且放入黑色集合
-
如果灰色对象有一个或多个指针指向白色对象,那么所指向的白色对象将被标记为灰色,并放入灰色集合
-
只要灰色对象存在,这个过程就会一直存在
-
在过程中,如果灰色对象变为不可达对象,那么当次GC不会回收该对象,可能下次GC回收
-
-
最后灰色集合为空,白色集合就是需要清除的对象
写屏障
在GC过程中,会运行一个修改器程序mutator,该修改器会在适当的时候执行写屏障write barrier
保持黑色集合总没有任何对象指针指向白色集合对象,必须保持这个状态,否则GC会出现问题
-
执行时机
每次堆中的指针被修改,意味着该对象是可达的,写屏障都会去执行,将其标记为灰色并且放入灰色集合
mutator保持黑色集合中没有任何对象指向白色集合的对象,是靠写屏障的方法
深入GC
前提
Go的垃圾回收器关注点是低延迟。程序运行一般过程是创建对象,用指针操作存活对象,所以在这个过程中,可能创建出不会再被访问到的对象(没有指针指向的对象),即为垃圾对象,GC就是清理这些对象,释放空间。
标记-清除算法
垃圾回收使用最简单的就是标记清除算法,算法是遍历和标记(Mark)所有可达对象。但会将程序终止(STW),然后再去清扫不可达对象(Sweep)
-
在Mark阶段,对象被标记为白色、灰色、黑色
-
灰色的可达对象(子对象)被标记为灰色,其本身被标记为黑色
-
没有更多的灰色对象,就会开始Sweep阶段
STW问题
三色标记清除原理简单,但是会有STW存在,会增加延迟
STW解决
Go将垃圾处理器作为并发处理过程并且配合三色标记清除算法,来降低延迟
并发问题
-
并发过程可能会移动指针或者创建对象,这会让垃圾处理器处理困难
-
所以让三色算法并发运行的关键点是:维持标记清除算法的不变要素,即没有黑色对象能指向白色对象
-
可以简单理解灰色集合是白色集合与黑色集合之间的屏障
策略
-
新对象必须进入灰色集合,维持不变要素
-
程序中指针移动,指向的对象必须标记为灰色
-
每次移动指针,就会自动执行一些代码,也就是写屏障,会重新去标色
总结
-
Go采用三色标记-清除算法 + 写屏障机制作为整个GC算法
-
Go的GC处理器是一个实时的垃圾处理器,与其他的g一起并发运行
defer
defer的作用是注册函数并且是当外围函数返回之后执行的延迟函数
多个defer注册函数的情况下,安装LIFO顺序执行
func main() {
for i := 3; i > 0; i-- {
defer fmt.Print(i)
}
for i := 3; i > 0; i-- {
defer func() {
fmt.Print(i)
}()
}
// 通常避免其他问题发生,都是使用第三种方式
for i := 3; i > 0; i-- {
defer func(i int) {
fmt.Print(i)
}(i)
}
}
Panic与Recover
panic终止当前流程,并且panicking
recover收回使用了panic的goroutine的控制权,并且捕获panicking
recover只能使用在defer中
func main() {
defer func() {
if err := recover(); err != nil {
log.Println(err)
}
}()
panic("test panic")
}

浙公网安备 33010602011771号