dirty 提升为 read 的触发条件是 misses >= len(dirty) 且 read.amended == true;此时原子更新 read、置 dirty = nil、重置 misses = 0。

sync.Map 的 dirty 到 read 提升触发条件是什么
触发 dirty 向 read 提升(即“升级”或“promote”)的唯一时机是:当 read 中某个 key 未命中,且 misses 计数器达到 dirty 当前长度时(len(dirty)),才会执行一次 dirty → read 的全量拷贝,并重置 misses = 0。
注意:不是每次写入都触发,也不是按时间或周期触发;它纯靠读多写少场景下的“未命中累积”驱动。这是为了平衡读性能(避免每次读都锁 dirty)和内存冗余(不盲目复制)。
-
read是原子可读的map[interface{}]interface{},无锁访问 -
dirty是带互斥锁的普通 map,写操作(Store、Delete)必须加锁 - 只有
Load未在read中找到 key 时,才会计一次miss - 一旦
misses >= len(dirty),就执行read, dirty = dirty, nil(实际是深拷贝 key/value,且保留原dirty的 deleted 标记)
为什么 dirty 转 read 不直接交换指针而是深拷贝
因为 read 必须保证无锁安全读取,而 dirty 正在被其他 goroutine 并发修改(比如另一个 goroutine 正在 Store 新 key)。如果只交换指针,read 就会暴露未同步的中间状态(如部分更新的 map 内部结构),违反内存模型。
所以 Go runtime 在提升时会遍历整个 dirty,对每个非 nil value(即未被逻辑删除的 entry)做浅拷贝——key 和 value 指针都直接复用,但 map 结构本身是新分配的。这也意味着:
- value 是指针类型时,不会额外分配内存;是 struct 时也不会复制字段内容,只复制 struct 值本身(即值语义)
- 如果
dirty中有大量已被Delete的 key(对应 entry.value == nil),它们不会进入新read - 拷贝过程持有
m.mu锁,因此会短暂阻塞所有写操作,但读操作仍可通过旧read继续进行
read 中的 entry.value == expunged 表示什么,会影响 dirty 提升吗
expunged 是一个私有哨兵值(var expunged = unsafe.Pointer(new(interface{}))),表示该 entry 曾属于 dirty,但已在 dirty 提升为 read 后被 Delete,且此后再未被 Store 过。此时该 entry 在 read 中保留 key,但 value 固定为 expunged,且不可恢复。
它**不影响** dirty → read 提升逻辑本身,但影响提升后的行为:
- 提升时,
expunged条目会被跳过(不复制进新read),因为它的 value 已失效 - 后续对该 key 的
Load会返回 false(未找到),即使它还在read的 map 中(因 value == expunged) - 只有当该 key 再次
Store时,才会先在dirty中新建 entry(此时dirty被懒创建),不再动read中那个expunged条目
实战中容易误判的两个典型现象
开发者常以为 “刚 Store 的 key 立刻能 Load 到”,结果偶发失败——这往往不是 bug,而是 read 还没提升,且当前 goroutine 恰好走的是 dirty 分支(比如之前有写导致 dirty 非空,但 read 未更新)。
- 现象一:
Store("a", 1); Load("a")返回 false —— 可能因read.amended == false且read中无该 key,但dirty也尚未被提升;此时Load会尝试读dirty(需锁),但如果dirty为空或该 key 真不在其中,就返回 false - 现象二:高并发下
misses累积极快,导致频繁提升,CPU 花在 map 拷贝上 —— 应检查是否误将sync.Map当作通用 map 使用(比如高频遍历、大量随机写);它只适合读远多于写的场景(如配置缓存、连接池元信息) - 调试建议:用
go tool trace观察sync.Map.Load是否长期卡在mutex上,或用runtime.ReadMemStats看是否出现大量小对象分配(提示频繁提升)
真正需要控制提升时机的场景极少;与其干预内部逻辑,不如确认使用模式是否匹配 sync.Map 的设计前提——读多、写少、key 稳定、不需遍历。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











