为什么协程比线程轻量
- go 协程默认的栈空间内存大小只有 2 KB (上限1 GB)
- 线程切换需要进行系统调用,开销比普通函数调用大很多,协程在用户态实现没这个开销
- 协程数据结构小
- 协程切换不需要保存寄存器信息,协程通过栈来传递变量
GMP
M 代表一个底层的操作系统线程
P 协程的管理者, Go 语言抽象的一个处理器,运行时会绑定一个可运行的 M,当 M 不可运行(例如陷入系统调用)时 P 会带着 G 队列去投奔另外的 M。
G
Go 协程调度
Go 协程调度本质上是给绑定在 P 的 M 选择下一个 G 来运行的过程
P 中存在一个协程的等待队列,这些协程会按先进先出的顺序被 M 执行。同时 Go 会启动一个特殊的不绑定 P 的线程:sysmon(System monitor),sysmon 的一部分职责是遍历所有的 P,将在 P 上运行超过 10ms 的协程标记为可抢占。然后在 G 在函数调用时就会触发抢占调度,重新回到运行队列排队。
P 和 M 并不是一一对应的,比如当底层线程 M 陷入系统调用时, P 会带着它所有的 G 重新绑定空闲的 M
在 P 上的队列没有可以运行的协程时,会尝试去全局协程队列中寻找,如果还是没有,会随机的尝试从其他 P 中窃取一半的协程到当前队列,再从当前队列中获取。
slice 扩容机制
-
所需容量(need cap)大于old cap 的两倍,设置 cap = need cap
-
need cap 小于 old cap 的两倍
- 旧的 slice len 小于 1024,则申请容量为旧的容量的两倍
- 旧的 slice len 大于 1024 ,new cap = old cap + 1/4 old cap,直到 new cap 大于所需容量
map和sync.map
GC
三色标记法
1、首先创建三个白、灰、黑三个集合,白色表示未被 mark 和 scan 的对象;灰色表示已经被 mark 但是未被 scan 的对象;黑色表示已经被 mark 和 scan 的对象;
2、初识时所有对象都在白色集合。
3、从根节点出发开始广度遍历,将其引用的对象加入灰色集合。
4、遍历灰色集合,将灰色对象引用的白色对象放入灰色集合,再将此灰色对象放入黑色集合。
标记过程中通过 write-barrier 检测对象引用的变化。重复 4 直到灰色集合中没有任何对象时 GC 结束。 此时黑色为存活对象,白色为回收对象。
参考资料 : www.jianshu.com/p/bfc3c65c0…