go 基本数据结构

1string    16字节
2byte      1
3bool      1
4int    看机器
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的零值

 

posted @ 2020-01-30 20:42  慕沁  阅读(828)  评论(0)    收藏  举报