1.初始化时数组必须指明长度,slice 不用
2.数组[4]int 和 [5]int 是不同的类型
3.数组传递是将数组整个拷贝进行传递,函数内修改不影响外面数组的值,而切片则是对引用复制的传递,如果修改了切片有可能会影响外面的切片。
4.数组的长度不可变,但是Slice可以进行扩容
5.用slice截取数组时,修改slice会影响到数组。因为slice就是对原数组某一段的引用。
Golang 中的变量只要被引用就会一直存活,Golang 编译器会将函数的局部变量分配到栈帧上,如果编译器不能确保变量return 后不再被引用,那么编译器会将它分配到堆上 并且如果局部变量非常大,那么它需要被分配在堆上而不是栈上。 并且Go 分成了微对象(0,16B)、小对象(16B,32KB)、大对象(32KB ,+∞),微对象通过微分配器提高分配的性能,大对象会分配到栈上。
函数名首字母小写,表示private;
函数名首字母大写,表示public。
go 提供的sync.map。适合于读多写少的场景。对于写多的场景,会导致 read map 缓存失效,需要加锁,导致冲突变多;而且由于未命中 read map 次数过多,导致 dirty map 提升为 read map,这是一个 O(N) 的操作,会进一步降低性能。
每个Gotoutine对应一个G结构体,G存储Goroutine的运行堆栈,状态,以及任务函数,可重用(函数实体)G需要保存到P才能被调度执行 ,os内核线程抽象,代表真正执行计算的,在绑定有效的P后,进入schedule循环 M的数量是不固定的,有Go Runtime调整,为了防止创建过多OS线程导致系统调度不过来,目前默认设置为10000个,M不保存G的上下文,这是G可以跨M的基础。 表示逻辑处理器,对G来说,P相当于CPU核,G只有绑定到P才能被调度。对M来说,P提供了相关的执行环境,入内存分配状态,任务队列等。 P 的数量决定了系统内最大可并行的 G 的数量(前提:物理 CPU 核数 >= P 的数量)。 P 的数量由用户设置的 GoMAXPROCS 决定,但是不论 GoMAXPROCS 设置为多大,P 的数量最大为 256。
- 调度器和锁是全局资源,所有的调度状态都是中心化存储的,锁竞争问严重;
- 线程需要经常互相传递可运行的 Goroutine,引入了大量的延迟;
- 每个线程都需要处理内存缓存,导致大量的内存占用并影响数据局部性;
- 系统调用频繁阻塞和解除阻塞正在运行的线程,增加了额外开销;
- 如果对象中没有引用类型,那么直接浅拷贝(直接申请一个变量赋值)即可,如果有,那么需要对引用类型进行一个拷贝,不然仍然会指向同一个引用。可以基于序列化和反序列化来实现对象的深度拷贝:
func deepCopy(dst, src interface{}) error { var buf bytes.Buffer if err := gob.NewEncoder(&buf).Encode(src); err != nil { return err } return gob.NewDecoder(bytes.NewBuffer(buf.Bytes())).Decode(dst) }