Loading

Mastering Go 内部运行机制—垃圾回收?defer?

Chapter2

认识Go程序运行的内部机制

本章概览

介绍垃圾回收以及其运行机制、与C代码相互调用、panic、defer、recover

  1. 编译器

  2. 垃圾回收工作原理、以及如何检测垃圾回收情况

  3. 在Go中调用C,C中调用Go

  4. panic、recover

  5. unsafe包

  6. defer

  7. Linux工具strace、FreeBSD工具dtrace

  8. Go环境信息

  9. 节点树

  10. Go汇编

编译器

Go编译器需要在go tool的帮助下执行

  1.  编译

结果是一个包含目标代码的二进制文件,但是不能直接运行,只是目标代码的""直译"

   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
  1. 检测竞态条件

在并发编程,可能涉及资源竞争、锁等问题,需要检测竞态条件

   go tool compile -race go_tool.go
  1. 汇编

  go tool compile -S go_tool.go

垃圾回收

更多信息查看runtime下mgc.go文件

  1. 目标

检测哪些对象超出范围并且不会被引用到,然后释放该空间2. 特点

  2.  特点

  1. 垃圾回收是和程序并行的,和调度器一起工作

  2. 写屏障

  3. 非分代和非压缩

三色标记-清除算法

Go的垃圾回收器基于三色标记清除算法

  1. 三色标记清除算法 + 写屏障

  2. 垃圾回收器与Go的调度器并发执行

  3. 首要原则:把堆中的对象按照颜色分到不同的集合,颜色根据算法标记

三色集合
  1. 黑色集合

    黑色集合中对象确保没有任何指针指向白色集合中对象

  2. 白色集合

    白色集合中允许有指针指向黑色集合对象, 不会对垃圾回收造成影响;白色集合中对象就是垃圾回收的对象

  3. 灰色集合

    灰色集合可能会有指针指向白色集合对象

注意:没有黑色对象能进入白色集合,这允许算法去操作并清除白色集合对象、并且黑色对象不能直接指向白色集合中的对象

回收流程
  1. 当垃圾回收开始时,全部对象标记为白色

  2. 然后垃圾回收器遍历所以根对象,将其标记为灰色

    根对象就是程序能直接访问的对象,全局变量以及栈里面

  3. 选取一个灰色对象,将其标记为黑色,然后去寻找该对象是否有指针指向白色集合的对象

    1. 灰色对象被其他对象所指向,那么灰色对象就会被标记为黑色,并且放入黑色集合

    1. 如果灰色对象有一个或多个指针指向白色对象,那么所指向的白色对象将被标记为灰色,并放入灰色集合

    1. 只要灰色对象存在,这个过程就会一直存在

    2. 在过程中,如果灰色对象变为不可达对象,那么当次GC不会回收该对象,可能下次GC回收

  1. 最后灰色集合为空,白色集合就是需要清除的对象

写屏障

在GC过程中,会运行一个修改器程序mutator,该修改器会在适当的时候执行写屏障write barrier

  1. 作用

保持黑色集合总没有任何对象指针指向白色集合对象,必须保持这个状态,否则GC会出现问题

  1. 执行时机

每次堆中的指针被修改,意味着该对象是可达的,写屏障都会去执行,将其标记为灰色并且放入灰色集合

mutator保持黑色集合中没有任何对象指向白色集合的对象,是靠写屏障的方法 

深入GC

前提

Go的垃圾回收器关注点是低延迟。程序运行一般过程是创建对象,用指针操作存活对象,所以在这个过程中,可能创建出不会再被访问到的对象(没有指针指向的对象),即为垃圾对象,GC就是清理这些对象,释放空间。

标记-清除算法

垃圾回收使用最简单的就是标记清除算法,算法是遍历和标记(Mark)所有可达对象。但会将程序终止(STW),然后再去清扫不可达对象(Sweep)

  1. 在Mark阶段,对象被标记为白色、灰色、黑色

  2. 灰色的可达对象(子对象)被标记为灰色,其本身被标记为黑色

  3. 没有更多的灰色对象,就会开始Sweep阶段

STW问题

三色标记清除原理简单,但是会有STW存在,会增加延迟

STW解决

Go将垃圾处理器作为并发处理过程并且配合三色标记清除算法,来降低延迟

并发问题

  1. 并发过程可能会移动指针或者创建对象,这会让垃圾处理器处理困难

  2. 所以让三色算法并发运行的关键点是:维持标记清除算法的不变要素,即没有黑色对象能指向白色对象

  3. 可以简单理解灰色集合是白色集合与黑色集合之间的屏障

策略

  1. 新对象必须进入灰色集合,维持不变要素

  2. 程序中指针移动,指向的对象必须标记为灰色

  3. 每次移动指针,就会自动执行一些代码,也就是写屏障,会重新去标色

总结

  1. Go采用三色标记-清除算法 + 写屏障机制作为整个GC算法

  2. Go的GC处理器是一个实时的垃圾处理器,与其他的g一起并发运行

defer

  1. defer的作用是注册函数并且是当外围函数返回之后执行的延迟函数

  2. 多个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

  1. panic终止当前流程,并且panicking

  2. recover收回使用了panic的goroutine的控制权,并且捕获panicking

  3. recover只能使用在defer中

 func main() {
  defer func() {
  if err := recover(); err != nil {
  log.Println(err)
  }
  }()
  panic("test panic")
 }

 

posted @ 2020-12-16 10:07  God-Yao  阅读(94)  评论(0)    收藏  举报