go语言map扩容 go语言map扩容原理

Go语言——sync.Map详解 sync.Map是1.9才推荐的并发安全的map,除了互斥量以外 , 还运用了原子操作,所以在这之前,有必要了解下 Go语言——原子操作
【go语言map扩容 go语言map扩容原理】 go1.10\src\sync\map.go
entry分为三种情况:
从read中读取key , 如果key存在就tryStore 。
注意这里开始需要加锁,因为需要操作dirty 。
条目在read中 , 首先取消标记,然后将条目保存到dirty里 。(因为标记的数据不在dirty里)
最后原子保存value到条目里面,这里注意read和dirty都有条目 。
总结一下Store:
这里可以看到dirty保存了数据的修改,除非可以直接原子更新read,继续保持read clean 。
有了之前的经验 , 可以猜测下load流程:
与猜测的 区别 :
由于数据保存两份,所以删除考虑:
先看第二种情况 。加锁直接删除dirty数据 。思考下貌似没什么问题,本身就是脏数据 。
第一种和第三种情况唯一的区别就是条目是否被标记 。标记代表删除 , 所以直接返回 。否则CAS操作置为nil 。这里总感觉少点什么 , 因为条目其实还是存在的,虽然指针nil 。
看了一圈貌似没找到标记的逻辑,因为删除只是将他变成nil 。
之前以为这个逻辑就是简单的将为标记的条目拷贝给dirty,现在看来大有文章 。
p == nil,说明条目已经被delete了,CAS将他置为标记删除 。然后这个条目就不会保存在dirty里面 。
这里其实就跟miss逻辑串起来了 , 因为miss达到阈值之后,dirty会全量变成read,也就是说标记删除在这一步最终删除 。这个还是很巧妙的 。
真正的删除逻辑:
很绕 。。。。
goland map底层原理map 是Go语言中基础的数据结构,在日常的使用中经常被用到 。但是它底层是如何实现的呢?
总体来说golang的map是hashmap,是使用数组+链表的形式实现的,使用拉链法消除hash冲突 。
golang的map由两种重要的结构,hmap和bmap(下文中都有解释),主要就是hmap中包含一个指向bmap数组的指针,key经过hash函数之后得到一个数,这个数低位用于选择bmap(当作bmap数组指针的下表) , 高位用于放在bmap的[8]uint8数组中 , 用于快速试错 。然后一个bmap可以指向下一个bmap(拉链) 。
Golang中map的底层实现是一个散列表,因此实现map的过程实际上就是实现散表的过程 。在这个散列表中,主要出现的结构体有两个 , 一个叫 hmap (a header for a go map),一个叫 bmap (a bucket for a Go map,通常叫其bucket) 。这两种结构的样子分别如下所示:
hmap :
图中有很多字段,但是便于理解map的架构,你只需要关心的只有一个,就是标红的字段: buckets数组。Golang的map中用于存储的结构是bucket数组 。而bucket(即bmap)的结构是怎样的呢?
bucket :
相比于hmap,bucket的结构显得简单一些,标红的字段依然是“核心” , 我们使用的map中的key和value就存储在这里 。“高位哈希值”数组记录的是当前bucket中key相关的“索引”,稍后会详细叙述 。还有一个字段是一个指向扩容后的bucket的指针 , 使得bucket会形成一个链表结构 。例如下图:
由此看出hmap和bucket的关系是这样的:
而bucket又是一个链表,所以,整体的结构应该是这样的:
哈希表的特点是会有一个哈希函数,对你传来的key进行哈希运算,得到唯一的值,一般情况下都是一个数值 。Golang的map中也有这么一个哈希函数,也会算出唯一的值,对于这个值的使用,Golang也是很有意思 。

推荐阅读