当前位置: 首页 > article >正文

go-map+sync.map的底层原理

map

哈希冲突解决方式

1.拉链法

2.开放地址法

底层结构

        Go 的 map 在源码中由 runtime.hmap 结构体表示,buckets-指向桶数组的指针(常规桶),oldbuckets-扩容时指向旧桶数组的指针。

type hmap struct {count     int    // 当前元素个数(len(map) 直接返回此值)flags     uint8  // 状态标志(是否正在扩容、迭代等)B         uint8  // 桶的数量为 2^B(哈希桶数量的对数)noverflow uint16 // 溢出桶的大致数量hash0     uint32 // 哈希种子,用于计算键的哈希值buckets    unsafe.Pointer // 指向桶数组的指针(常规桶)oldbuckets unsafe.Pointer // 扩容时指向旧桶数组的指针nevacuate  uintptr        // 扩容时记录下一个要迁移的桶编号extra *mapextra // 可选字段,用于管理溢出桶
}

bmap结构 

type bmap struct {// 1. 哈希值的高8位数组(快速比较)tophash [bucketCnt]uint8    // bucketCnt默认为8,每个桶最多存8个键值对// 2. 键数组(具体类型由map定义决定)keys    [bucketCnt]KeyType  // 例如int、string等// 3. 值数组(具体类型由map定义决定)values  [bucketCnt]ValueType// 4. 溢出桶指针(链表结构,处理哈希冲突)overflow *bmap
}

tophash [bucketCnt]uint8    // bucketCnt默认为8,每个桶最多存8个键值对

作用​​:存储每个键哈希值的前8位,用于快速过滤不匹配的键。一个key会通过哈希函数得到一个哈希值(高八位就是tophash ),哈希值再对map的数量取模得到桶下标,找到自己存放的位置。

keys    [bucketCnt]KeyType  // 例如int、string等

values  [bucketCnt]ValueType

  • ​容量​​:每个bmap最多存储​​8个键值对​​。
  • ​内存排列​​:
    • ​键和值分开存储​​:键数组(keys)和值数组(values)在内存中连续分布。
    • ​对齐优化​​:根据键和值的大小调整内存对齐,减少碎片。

overflow *bmap

  • ​作用​​:当桶已满时,新键值对存入溢出桶,形成链表。
  • ​示例​​:
    • 插入第9个键值对时,创建新的bmap,链接到overflow指针。
    • 查找时需遍历链表直到找到匹配项或链表末尾。

为什么键和值要分开存放?

  • ​键和值分开存储​​:键数组(keys)和值数组(values)在内存中连续分布。
  • ​对齐优化​​:根据键和值的大小调整内存对齐,减少碎片。

map中一个key的查找过程?

1. ​​哈希计算与桶定位​

  1. 计算键的哈希值 hash := hashFunc(key, h.hash0)
  2. 通过掩码运算定位桶:bucketIndex := hash & (2^B - 1)B为桶数量的指数)。
  3. 确定目标桶 bmap

2. ​​桶内查找与插入​

​遍历tophash数组,寻找空槽或匹配的高8位:

  • ​空槽​​:tophash[i] == 0,表示可插入新键值对。
  • ​匹配​​:tophash[i] == hashHigh,需进一步比较键是否相等。如果key不相等说明槽位被占了,那么查找下一个为空的槽位,如果找到最后发现槽位被占满那么就通过overflow 创建新的bmap插入数据。

go语言中的map怎么解决哈希冲突的?

利用了两种方法拉链法和开放法,看上面的题目回答。

map中一个key的删除过程?

        通过哈希值的高八位查找到一个桶的tophash位置,然后把tophash标记为emptyOne的状态,如果当前tophash位置后面所有的位置为空,那么就将当前位置标记为emptyReset状态,这样如果下一次遍历到了emptyReset就不会往后进行查找,提高了查询的效率。

map的扩容

扩容时机

        负载因子超标​​,负载因子超过为 ​​6.5​​触发双倍扩容。一个桶能存放八个元素,负载因子为6.5的时候表示一个桶中位置快被分配完了,一个桶快满了,很有可能需要遍历溢出桶,那么就会触发双倍扩容。

        溢出桶的数量过多的话就触发等量扩容。插入元素过多就会有溢出桶,然后又删除元素,但是map的删除元素不会释放内存,再进行元素的插入,这样会导致元素的排列很松散。所以需要等量扩容重新排列元素位置,让内存更加紧密。

等量扩容

        等量扩容:并不扩大容量,buckets数量维持不变,重新做一遍搬迁动作,把松散的键值对重新排列一次,使得同一个 bucket 中的 key 排列地更紧密,节省空间,提高 bucket 利用率,进而保证更快的存取。

双倍扩容

双倍扩容:新建一个buckets数组,新的buckets数量大小是原来的2倍,然后I日buckets数据搬迁到新的buckets.

扩容方式?

        map采用渐进式扩容的方式,再插入修改key的时候,会进行元素的搬迁,每一次搬迁1-2个元素,从旧桶中找到一个当前访问的key-value,还有一个是通过迁移编号找到的key-value值,每次移动两个元素,再到新桶中做哈希运算重新放入新桶。

syncmap

        Go 语言中的 sync.Map 是专为并发场景设计的线程安全 map,其底层结构通过 ​​读写分离​​ 和 ​​无锁原子操作​​ 优化性能,尤其适合读多写少的场景。

底层结构

type Map struct {mu     sync.Mutex          // 互斥锁,保护 dirty 的并发写操作read   atomic.Value        // 存储只读数据(readOnly 结构),通过原子操作访问dirty  map[interface{}]*entry  // 存储可写数据,访问时需要加锁misses int                 // 记录从 read 读取失败的次数,触发 dirty 提升为 read
}
type readOnly struct {m       map[interface{}]*entry  // 存储键值对的指针(entry)amended bool                     // 标记 dirty 中是否有 read 中不存在的键
}

 如果amend为true那么就从dirty当中操作.

type entry struct {p unsafe.Pointer  // 指向实际值的指针(可能为 nil、expunged 标记或具体值)
}

entry 的状态管理,​p有三种状态, ​

  • ​正常值​​:entry.p 指向实际值。
  • ​删除标记​​:entry.p = nil(逻辑删除)。
  • ​完全删除​​:entry.p = expunged(一个特殊标记指针),表示键已从 dirty 移除。

有几种方法

Store(),更新插入一个key-value

Load(),返回对应的value

Delete(),删除

Range(),对map进行遍历

sync.map插入流程

  • 如果read当中不包含这个元素的话,那么就对dirty进行操作,获取锁操作dirty。
  • 如果read当中有这个元素并且对应entry指向的状态为nil或者为正常值,直接修改read的值
  • 如果read当中有这个元素并且对应entry指向的状态为expunged的状态的话,那么不光要在read当中进行修改,还要在dirty当中插入新值。

sync.map删除流程

read当中找到对应的数据,然后通过entry指针把指向的value置为nil。

如果read当中找不到对应的值,但是存在于dirty,就把dirty当中对应的entry指针指向expunged状态,表示键已从 dirty 移除。

read map和dirty map怎么互相转换的?

dirty->read

​从 read 无锁读取​​:

  • 通过原子操作获取 read.m,查找键对应的 entry
  • 若找到且 entry.p != nil,直接返回值

如果没有找到,那么加锁访问 dirty​,从 dirty 读取,并增加 misses 计数。

        当 misses >= len(dirty) 时,将 dirty 提升为 read,重置 dirty把dirty置空 和 misses

read->dirty 

     考虑到dirty->read的情况,在最后将 dirty 提升为 read后,会重置 dirty 把dirty置空。此时dirty为空,read不为空。

  如果要插入新的数据那么只能从dirty中进行插入,但此时dirty为空,所以dirty需要复制read当中的数据(这个过程就是dirty的重塑),把read当中value的状态=nil的值都标记成expunged状态,那么dirty不会对expunged状态的值进行复制。

相关文章:

go-map+sync.map的底层原理

map 哈希冲突解决方式 1.拉链法 2.开放地址法 底层结构 Go 的 map 在源码中由 runtime.hmap 结构体表示,buckets-指向桶数组的指针(常规桶),oldbuckets-扩容时指向旧桶数组的指针。 type hmap struct {count int // 当前元素个数(len…...

java怎么找bug?Arthas原理与实战指南

Arthas原理与实战指南 1. Arthas简介 Arthas是阿里巴巴开源的Java诊断工具,其名字取自《魔兽世界》的人物阿尔萨斯。它面向线上问题定位,被广泛应用于性能分析、定位问题、安全审计等场景。Arthas的核心价值在于它能够在不修改应用代码、不重启Java进程…...

Windows使用SonarQube时启动脚本自动关闭

一、解决的问题 Windows使用SonarQube时启动脚本自动关闭,并发生报错: ERROR: Elasticsearch did not exit normally - check the logs at E:\Inori_Code\Year3\SE\sonarqube-25.2.0.102705\sonarqube-25.2.0.102705\logs\sonarqube.log ERROR: Elastic…...

Day53 二叉树的层序遍历

给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* T…...

物联网智慧教室项目(完整版)

物联网智慧教室项目(一):智慧教室项目解决方案 一、智慧教室项目设计 (一)环境信息采集控制功能 1、硬件设计 使用STM32开发板模拟灯光控制,报警控制,光照信息采集: 灯光控制通过GPIO控制板载LED报警控…...

替代升级VMware | 云轴科技ZStack构建山西证券一云多芯云平台

通过云轴科技ZStack Cloud云平台,山西证券打造了敏捷部署、简单运维的云平台,不仅兼容x86、海光、鲲鹏三种异构服务器实现一云多芯,还通过云平台虚拟化纳管模块纳管原有VMware虚拟化资源,并对接第三方集中式存储,在保护…...

计算机网络期中复习笔记(自用)

复习大纲 –第一章 概述 计算机网络的组成 网络边缘:主机和网络应用程序(又称为“端系统”) 端系统中运行的程序之间的通信方式可划分为两大类: 客户/服务器方式(C/S方式) 对等方式(P2P方式…...

14.Chromium指纹浏览器开发教程之WebGL指纹定制

WebGL指纹概述 当在浏览器打开的网页上浏览内容时,看到的大多是平面的、静态的图像和文字。但是有时想要在网页上看到更加生动、立体的图像,如3D游戏、虚拟现实应用等。这时,就需要用到WebGL。 简单来说,WebGL(Web G…...

GitHub SSH连接终极解决方案

GitHub SSH连接终极解决方案:443端口修改多场景故障排查指南 一、问题现象速查 当开发者执行以下命令时出现连接异常: ssh -T gitgithub.com常见报错类型: 经典端口阻塞ssh: connect to host github.com port 22: Connection refused密钥验…...

Git 中修改某个特定的commit提交内容

在 Git 中修改某个特定的提交(commit)通常需要使用 交互式变基(Interactive Rebase) 或 修改提交(Commit Amend)。以下是不同场景下的具体操作步骤: 一、修改最近的提交(最新提交&am…...

每日算法【双指针算法】(Day 1-移动零)

双指针算法 1.算法题目(移动零)2.讲解算法原理3.编写代码 1.算法题目(移动零) 2.讲解算法原理 数组划分,数组分块(快排里面最核心的一步)只需把0改为tmp 双指针算法:利用数组下标来…...

B端管理系统:企业运营的智慧大脑,精准指挥

B端管理系统的定义与核心功能 B端管理系统(Business Management System)是专门设计用于支持企业内部运作和外部业务交互的一套软件工具。它集成了多种功能模块,包括但不限于客户关系管理(CRM)、供应链管理(SCM)、人力资源管理(HRM)以及财务管…...

使用Java基于Geotools的SLD文件编程式创建与磁盘生成实战

前言 在地理信息系统(GIS)领域,地图的可视化呈现至关重要,而样式定义语言(SLD)文件为地图元素的样式配置提供了强大的支持。SLD 能够精确地定义地图图层中各类要素(如点、线、面、文本等&#x…...

Git 命令速查手册

听说用美图可以钓读者? 一、基础操作核心命令 1. 仓库初始化与克隆 命令作用示例git init创建新仓库git init my-projectgit clone克隆远程仓库git clone [https://github.com/user/repo.git](https://github.com/user/repo.git)git remote add关联远程仓库git re…...

PKI 公钥基础设施

PKI 的全称是公钥基础设施(Public Key Infrastructure),是一个基于公钥加密技术,为网络环境中的各种应用提供安全服务的基础设施,由多个部分组成,各部分协同工作以实现数字证书的管理、密钥的生成与管理以及…...

android测试硬件工具 安卓硬件测试命令

Android开发常用ADB命令大全 在Android开发过程中,ADB(Android Debug Bridge)是一个非常重要的调试工具。掌握这些命令可以大大提高开发效率。如果你正在使用克魔开发助手(Keymob)这样的开发工具,你会发现它已经集成了很多ADB功能,让调试变得…...

网络编程 - 4 ( TCP )

目录 TCP 流套接字编程 API 介绍 SeverSocket Socket 用 TCP 实现一个回显服务器 服务端 客户端 运行调试 第一个问题:PrintWriter 内置的缓冲区 - flush 刷新解决 第二个问题:上述代码中,需要进行 close 操作吗? 第三…...

OSPF综合实验(HCIP)

1,R5为ISP,其上只能配置Ip地址;R4作为企业边界路由器, 出口公网地址需要通过ppp协议获取,并进行chap认证 2,整个OSPF环境IP基于172.16.0.0/16划分; 3,所有设备均可访问R5的环回&…...

真实波幅策略思路

该策略是一种基于ATR(Average True Range)指标的交易策略,主要用于期货市场中的日内交易。策略的核心思想是利用ATR指标来识别市场的波动范围,并结合均线过滤来确定买入和卖出的时机。 交易逻辑思维 1. 数据准备与初始化 - 集合竞…...

ESB —— 企业集成架构的基石:功能、架构与应用全解析

企业服务总线(Enterprise Service Bus,ESB)是一种重要的企业级集成架构,以下为你详细介绍: 一、概念与定义 ESB 是一种基于面向服务架构(SOA)的中间件技术,它充当了企业内部不同应…...

leetcode 674. Longest Continuous Increasing Subsequence

目录 题目描述 第一步,明确并理解dp数组及下标的含义 第二步,分析明确并理解递推公式 第三步,理解dp数组如何初始化 第四步,理解遍历顺序 代码 题目描述 这是动态规划解决子序列问题的例子。与第300题的唯一区别就是&#…...

STM32 外部中断EXTI

目录 外部中断基础知识 STM32外部中断框架 STM32外部中断机制框架 复用功能 重映射 中断嵌套控制器NVIC 外部中断按键控制LED灯 外部中断基础知识 STM32外部中断框架 中断的概念:在主程序运行过程中,出现了特点的中断触发条件,使得…...

Linux:基础IO---动静态库

文章目录 1. 动静态库前置知识1.1 动静态库知识回顾1.2 什么是动静态库 2. 动静态库2.1 站在库的制作者的角度2.2 站在库的使用者的角度2.3 动态库是怎么被加载的(原理) 序:上一篇文章我们从认识到理解,从理解到实现场景&#xff…...

深度学习-torch,全连接神经网路

3. 数据集加载案例 通过一些数据集的加载案例,真正了解数据类及数据加载器。 3.1 加载csv数据集 代码参考如下 import torch from torch.utils.data import Dataset, DataLoader import pandas as pd ​ ​ class MyCsvDataset(Dataset):def __init__(self, fil…...

SQL注入相关知识

一、布尔盲注 1、布尔盲简介 布尔盲注是一种SQL注入攻击技术,用于在无法直接获取数据库查询结果的情况下,通过页面的响应来判断注入语句的真假,从而获取数据库中的敏感信息 2、布尔盲注工作原理 布尔盲注的核心在于利用SQL语句的布尔逻辑…...

Codex CLI - 自然语言命令行界面

本文翻译整理自:https://github.com/microsoft/Codex-CLI 文章目录 一、关于 Codex CLI相关链接资源 二、安装系统要求安装步骤 三、基本使用1、基础操作2、多轮模式 四、命令参考五、提示工程与上下文文件自定义上下文 六、故障排查七、FAQ如何查询可用OpenAI引擎&…...

实现窗口函数

java 实现窗口函数 public class SlidingWin {public static void main(String[] args) {SlidingWin slidingWin new SlidingWin();double v slidingWin.SlidWin(2);System.out.println(v);}public double SlidWin(int k){int [] array new int[]{2,4,5,6,9,10,12,23,1,3,8…...

pycharm中怎么解决系统cuda版本高于pytorch可以支持的版本的问题?

在PyCharm中安装与系统CUDA版本不一致的PyTorch是可行的。以下是解决方案的步骤: 1. 确认系统驱动兼容性 检查NVIDIA驱动支持的CUDA版本:运行 nvidia-smi,右上角显示的CUDA版本是驱动支持的最高版本。只要该版本不低于PyTorch所需的CUDA版本…...

Day57 | 79. 单词搜索、89. 格雷编码

79. 单词搜索 题目链接&#xff1a;79. 单词搜索 - 力扣&#xff08;LeetCode&#xff09; 题目难度&#xff1a;中等 代码&#xff1a; class Solution {public boolean exist(char[][] board, String word) {char[] wordsword.toCharArray();for(int i0;i<board.lengt…...

清华《数据挖掘算法与应用》K-means聚类算法

使用k均值聚类算法对表4.1中的数据进行聚类。代码参考P281。 创建一个名为 testSet.txt 的文本文件&#xff0c;将以下内容复制粘贴进去保存即可&#xff1a; 0 0 1 2 3 1 8 8 9 10 10 7 表4.1 # -*- coding: utf-8 -*- """ Created on Thu Apr 17 16:59:58 …...