Go语言的特点
并发
高并发是Go语言的核心特性, 不同于传统的多进程或多线程,golang的并发执行单元是一种称为goroutine的协程。其在语言级别提供关键字:
- go——用于启动协程。
- chan——golang中用于并发的通道,用于协程的通信。
- select——golang提供的多路复用机制。
- close()——golang的内置函数,可以关闭通道。
- sync——golang标准库之一,提供锁。 协程经常被理解为轻量级线程,一个线程可以包含多个协程,共享堆不共享栈。协程间一般由应用程序显式实现调度,上下文切换无需下到内核层,高效不少。
协程间一般不做同步通讯,而golang中实现协程间通讯有两种:
共享内存型,即使用全局变量+mutex锁来实现数据共享; 消息传递型,即使用一种独有的channel机制进行异步通讯。
垃圾回收
Go采用的是标记清除方式。当GC开始时,从 root 开始一层层扫描,这里的root取当前所有 goroutine 的栈和全局数据区的变量(主要是这两个地方)。扫描过程中把能被触达的 object 标记出来,那么堆空间未被标记的 object 就是垃圾了(“可达性”近似等于“存活性”的思想);最后遍历堆空间所有 object 对垃圾(未标记)的object 进行清除,清除完成则表示 GC 完成。 清除的 object 会被放回到 mcache 中以备后续分配使用。
Go1.5的三色标记法
为了让标记过程也能并行,Go采用了三色标记+写屏障的机制。它的步骤如下:
GC 开始时,认为所有 object 都是“白色”,即垃圾 root 区的所有对象变为“灰色” 遍历所有“灰色”,将所有可达对象变为“灰色”,然后自身变为“黑色” 循环第3步,直到没有“灰色”。剩余的“黑色”是存活数据,“白色”都是垃圾 对于“黑色”,如果在标记期间发生了写操作,写屏障会在真正赋值前将新对象标记为“灰色”。 标记过程中,新分配的对象,都会变成“黑色” 还有一种情况: 标记过程中,堆上的 object 被赋值给了一个栈上指针,导致这个 object 没有被标记到。因为对栈上指针进行写入,写屏障是检测不到的
Go1.8 混合写屏障
三色标记方式,需要再最后重新扫描一遍所有全局变量和goroutie栈空间,如果系统的 goroutine 很多,这个阶段耗时也会比较长,甚至会长达 100ms。毕竟 goroutine 很轻量,大型系统中,上百万的 goroutine 也是常有的事情。
而混合写屏障,会在赋值前堆旧数据置灰,在视情况对新值进行置灰,这样就不需要在最后回头重新扫描所有的 goroutine 的栈空间了,这使得整个 GC过程STW几乎可以忽略不计。