go 基本数据结构
1、string 16字节 2、byte 1 3、bool 1 4、int 看机器
5、channel 8
6、slice 24字节(ptr,len,cap) 5、所有的空结构体都是同一个地址;结构体8字节对齐-->加速内存读取速度
结构体
runtime/string.go type stringStruct struct { str unsafe.Pointer len int }
字符串比较内容
修改字符串,且不改变原地址
1、根据切片的长度申请空间,加入内存地址为p,切片长度为len(b)
2、构建string (string.str=p;string.len=len;)
3、拷贝数据(切片中数据拷贝到新申请的内存空间)
拷贝 string->[]byte一次拷贝
[]byte ->string不会拷贝,只是字符串的指针属性,指向切片
byte切片转换成string的场景很多,为了性能上的考虑,有时候只是临时需要字符串的场景下,
byte切片转换成string时并不会拷贝内存,二是直接返回一个string,这个string的指针指向切片的内存 比如,编译器会识别如下的临时场景 1、使用m[string(b)]来查找map 2、字符串比较 string(b)>= 3、字符串拼接 因为是临时把byte切片转换成string,也就避免了因byte切片同容改成导致string引用失败的情况,
因此不必拷内存新建一个string
切片
runtime/slice.go type slice struct { array unsafe.Pointer len int cap int }
切片扩容大小 newcap := old.cap doublecap := newcap + newcap if cap > doublecap { newcap = cap } else { if old.len < 1024 { newcap = doublecap // 1024之前翻倍 } else { // Check 0 < newcap to detect overflow // and prevent an infinite loop. for 0 < newcap && newcap < cap { newcap += newcap / 4 } // Set newcap to the requested cap when // the newcap calculation overflowed. if newcap <= 0 { newcap = cap } } }
.. 下边还有根据切片类型计算的 操作
总结:
1、不足1024,双倍扩容;超过1024的,1.25倍扩容
2、若是多个元素,且双背后容量不能容纳,直接使用预估的容量
3、以上两条在得到新的容量之后,均需要根据slice的类型size,算出新的容量所需的内存情况,再向上取整,
得到新的所需内存,除上类型size,得到真正的最终容量,最为新的slice容量。
链接:https://studygolang.com/articles/19523
map
1、根据传入的key用对应的hash函数计算出哈希值 2、取哈希值的低B位(uint8类型的数字)定位是哪一个bucket 3、定位到bucket之后,取哈希值的高8位,和bucket中的uint[8]数组中存储的高8位进行比对,完全匹配根据数组的index在k,v字节数组中查找对应的key,
若匹配上则返回k,v;若没有完全匹配则继续比对高8位的值,当前bucket的高8位数组完全比对完,若有溢出bucket则继续比对,
若全部比对完未发现则当前map不含有此key
// bucket的结构
type hmap struct { count int // map中的元素个数,必须放在struct的第一个位置,因为内置len函数会从这里读取 flags uint8 B uint8 // 包含2^B(亦或)个bucket noverflow uint16 // 溢出的bucket个数 hash0 uint32 // hash种子 buckets unsafe.Pointer // buckets的数组指针 oldbuckets unsafe.Pointer // 结构扩容时用于复制的buckets数组 nevacuate uintptr // 搬迁进度(已经搬迁的buckets数量) extra *mapextra // optional fields }
channel 结构//path runtime/chan.go
type hchan struct { qcount uint // 当前队列中剩余元素个数 dataqsiz uint // 环形队列长度,即可以存放的元素个数 buf unsafe.Pointer // 环形队列指针 elemsize uint16 // 每个元素大小 closed uint32 // 标识关闭状态 elemtype *_type // 元素类型 sendx uint // 队列下表,指示元素写入时存放到队列中的位置 recvx uint // 队列下标,指示元素从队列的该位置读出 recvq waitq // 等待读消息的goroutine队列 sendq waitq // 等待写消息的goroutine队列 // // lock protects all fields in hchan, as well as several // fields in sudogs blocked on this channel. // // Do not change another G's status while holding this lock // (in particular, do not ready a G), as this can deadlock // with stack shrinking. lock mutex // 互斥锁,chan不允许并发读写 }
func makechan(t *chantype, size int) *hchan
向channel写数据
读数据
关闭channel
关闭channel时会把recvq中的G全部唤醒,本该写入G的数据位置为nil.
把sebdq中的G全部唤醒,但这些G会panic
func closechan(c *hchan)
只能关闭 可写channel (可关闭一次,否则panic)
关闭之后,再读取,会返回该channel的零值