Go语言map模型

结构

Go 语言采用的是哈希查找表,并且使用链表解决哈希冲突。

type hmap struct {
    // 元素个数,调用 len(map) 时,直接返回此值
    count     int
    flags     uint8
    // buckets 的对数 log_2
    B         uint8
    // overflow 的 bucket 近似数
    noverflow uint16
    // 计算 key 的哈希的时候会传入哈希函数
    hash0     uint32
    // 指向 buckets 数组,大小为 2^B
    // 如果元素个数为0,就为 nil
    buckets    unsafe.Pointer
    // 扩容的时候,buckets 长度会是 oldbuckets 的两倍
    oldbuckets unsafe.Pointer
    // 指示扩容进度,小于此地址的 buckets 迁移完成
    nevacuate  uintptr
    extra *mapextra // optional fields
}

tophash:记录哈希值高8位,为了快速遍历。

通过make初始化会调用runtime.makemap(B)。从数值B决定了要创建多少个桶例如B是3则创建8个正常桶。如下图,会多创建几个溢出痛,由extra的overflow指向下一个溢出桶。

通过自变量初始化。如下图两种赋值方式

map的访问 :

计算桶号,确定哪个bmap

计算tophash:,得到tophash后去刚才得到的桶里遍历得到tophash的项,再看看key是否是我们想要的key。

如果没找到则由overflow去溢出桶里再去遍历。

map扩容:

原因:溢出桶太多会扩容,2 装在因子超过6.5。

1,等量扩容。数据曾经很多现在都被删了,所以等量扩容整理整理。

2,翻倍扩容。

扩容步骤1,创建一组新桶。2,oldbuckets指向原有的桶数组。3 buckets指向新的桶数组。4 map标记扩容状态。

步骤2将所有数据从旧桶驱逐到新桶。渐进式驱逐。每操作一个旧桶,将旧桶数据驱逐到新桶。如下图例子,老的2号桶的数据将被分到新的2号桶和6号桶。

步骤3。老桶释放

map的并发问题

map不能读写并发。原因:A协程读老桶的数据,B协程驱逐了这个桶把这个桶的数据驱逐到新桶中,则A读到错误的数据。解决方案: 1: 给map加mutex锁。2: 使用sync.map。

sync.map的读写修改

这里key分别是 a b c。value对应 A B C。正常情况下的数据结构如下:

正常读写走下面这条线:

追加操作:1,先正常读read到m,发现没有d于是回来上锁。锁的是dirty map。对dirty map追加。追加后dirtymap 多了个d,并且read map amended=true表示有追加。

追加后的读写:1,先正常走read map 找不到,发现amended为true,于是去dirty map读。读完miss+=1,表示read map 没有 dirty map 有。当misses==dirty时候,会dirty提升。将原来read map删除。

再重建:

sync.map的删除。(正常删除,追加后删除,提升后被删的key的处理)

1,删除d D 将指针设置为nil

后面由GO的GC垃圾自动回收

2,追加删除。删除D d .同样先找read map发现没有,再找dirty map。发现在下面,设置为nil。

重建:

总结

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值