1.传入channel的值是原来的备份,从channel中取出来的值也是通道中值的备份
2.如果想通过channel传输同一个值,那么可以传递这个值的指针
3.如果关闭channel要从发送端关闭,如果从接收端关闭会引发恐慌
4.发送端关闭通道不会影响接收端接收
5.带缓冲区和不带缓冲区的channel区别就是长度是否为0,不带缓冲区的channel的长度就是0
6.操作未被初始化的通道会造成永久阻塞
指向桶数组的指针
指向旧桶数组的指针
B 2^B个桶的数量
溢出桶的数量
储存溢出桶的extra
先通过低B位找到是哪个编号的桶,在桶里面通过高八位进行循环判断,如果高八位相同了,在进行全部hash值的判断
gmp模型
在一开始使用的便是标记清除法
1.需要stw
2.标记需要扫描整个heap
3.清除数据会产生heap碎片
1.就是只要是新创建的对象,默认的颜色都是标记为“白色”.
2.每次gc开始从根节点开始遍历所有对象,把遍历过的节点从白色集合加入到灰色集合
3.遍历灰色集合,把灰色对象引用的白色对象 从白色集合放入灰色集合,之后将此灰色对象放入黑色集合
4.重复3,直到灰色中无任何对象
5.回收所有的白色标记表的对象,也就是回收垃圾
然而三色标记法也需要stw
否则以下两种情况同时发生的时候会导致 对象丢失现象
条件1.一个白色对象被黑色对象引用
条件2.灰色对象与他之间的可达关系的白色对象遭到了破坏(灰色同时丢失了该白色)
而且如果白色对象 还有很多对象的话,也会一并清理掉
而使用stw有着明显的资源浪费
于是屏障机制就踹出来了
不存在黑色对象引用白色对象的指针
所有被黑色对象引用的白色对象都处于灰色保护状态
插入屏障和删除屏障用来实现上面的两种不变性
在A对象引用B对象的时候,B对象被标记为灰色(将B挂在A下游,B必须被标记为灰色)
不存在黑色对象引用白色对象的情况了,因为白色会强制变成灰色
添加下游对象(当前下游对象slot, 新下游对象ptr) {
//1
标记灰色(新下游对象ptr)
//2
当前下游对象slot = 新下游对象ptr
}
黑色对象的内存槽有两种位置,栈和堆,栈空间的特点是容量小,但是要求响应速度快,又因为函数调用弹出频繁使用,所以插入屏障机制在栈空间中不使用,而仅仅使用在堆空间对象的操作中(为了响应速度考虑)
只能删除清除对空间中的对象
被删除的对象,如果自身为灰色或者白色,那么被标记为灰色
保护灰色对象到白色对象的路径不会断
添加下游对象(当前下游对象slot, 新下游对象ptr) {
//1
if (当前下游对象slot是灰色 || 当前下游对象slot是白色) {
标记灰色(当前下游对象slot) //slot为被删除对象, 标记为灰色
}
//2
当前下游对象slot = 新下游对象ptr
}
回收精度低,一个对象及时被删除了,最后一个指向他的指针也依旧可以活过这一轮,在下一轮gc中被回收掉
可见两者都有缺点,于是混合写屏障
插入写屏障:因为只能用堆空间上,结束时需要STW来重新扫描栈,标记栈上引用 的白色对象的存活
删除写屏障:回收精度低,gc开始时stw扫描堆栈来记录初始快照,这个过程会保护开始时刻所有存活的对象
go1.8的混合写屏障,避免了对栈的re-scan的过程,极大的减少了STW的时间,结合了两者的优点
1.gc开始将栈上的对象全部扫描并标记为黑色(之后不再进行第二次重复扫描,无需stw)
2.gc期间,任何在栈上创建的新对象,junweiheise
3.被删除的对象标记为黑色
4.被添加的对象标记为灰色
添加下游对象(当前下游对象slot, 新下游对象ptr) {
//1
标记灰色(当前下游对象slot) //只要当前下游对象被移走,就标记灰色
//2
标记灰色(新下游对象ptr)
//3
当前下游对象slot = 新下游对象ptr
}
屏障技术是不在栈上应用的,因为要保障栈的运行效率
整体过程需要启动stw,效率极低
堆空间启动写屏障,栈空间不启动,全部扫描后,需要重新扫描一遍栈(需要stw),效率普通
三色标记法,混合写屏障机制,栈空间不启动,堆空间启动。整个过程几乎需要stw,效率极高
更详细的看
https://www.jianshu.com/p/4c5a303af470
func goroutineRun(values []int) {
for value := range values {
go func() {
fmt.Println(value)
}()
}
}
实际是遍历数组的所欲变量,由于闭包只是绑定到这个value变量上,并没有保存到goroutine栈中,所以以上代码极有可能运行的结构都输出为切片的最后一个元素。因为这样写会导致for循环结束后才执行goroutine多线程操作,这时候value值只指向了最后一个元素。这样的结果不是我们所希望的,而且还会产生并发的资源抢占冲突,所以是非常不推荐这样写的
for val := range values {
go func(val interface{}) {
fmt.Println(val)
}(val)
}
这里val作为一个参数传入goroutine中,每个val都会被独立计算并保存到goroutine的栈中,从而得到预期的结果,另一种方法是循环内定义新的变量,由于在循环内定义的变量在循环遍历的过程中是不共享的,因此也可以达到相同的效果
for i := range valslice {
val := valslice[i]
go func() {
fmt.Println(val)
}()
}
111
ubie
111