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

从golang的sync.pool到linux的slab分配器

最近学习golang的时候,看到golang并发编程中有一个sync.pool,即对象池,猛地一看这不跟linux的slab分配器类似嘛,赶紧学习记录下

这里先总结下设计sync.pool和slab的目的

  • sync.pool
    • 为了缓解特定类型的对象频繁创建和销毁,导致gc压力大的问题
  • slab
    • linux使用伙伴系统管理内存,伙伴系统分配内存时以页Page(4K byte)为单位进行分配,而linux内核中,会频繁的创建struct page, struct fd,struct task的实例,这些结构体对象的size通常只有几十个字节,如果每次都从伙伴系统分配内存,将造成极大的内存浪费

sync.pool

参考了:

基本用法:https://geektutu.com/post/hpg-sync-pool.html

基本用法+源码分析:https://www.cnblogs.com/qcrao-2018/p/12736031.html

底层分析更全面:https://www.cyhone.com/articles/think-in-sync-pool/

sync.pool的特点

并发安全并且lock-free,sync.pool之所以能做到lock-free,跟golang的GMP调度模型有关系,下面再进行详细介绍

sync.pool的使用场景

当存在对象被频繁的重复分配时,可以使用sync.pool避免对象重复创建和销毁,减轻gc的压力

sync.pool的基本用法

创建对象池

var studentPool = sync.Pool{New: func() interface{} { return new(Student) },
}

理解:创建一个sync.pool对象,命名为studentPool,创建sync.pool对象时,传入的New成员是一个函数,该函数定义了创建student对象的行为。需要注意的是,sync.pool在初始化时只会创建一个对象,后续调用Get发现没有对象时,才会继续创建对象

向对象池添加/获取一个对象

stu := studentPool.Get().(*Student)
json.Unmarshal(buf, stu)
studentPool.Put(stu)

完整的例子

package mainimport ("fmt""sync"
)var pool *sync.Pooltype Person struct {Name string
}func initPool() {pool = &sync.Pool{New: func() interface{} {fmt.Println("Creating a new Person")return new(Person)},}
}func main() {initPool()p := pool.Get().(*Person)fmt.Println("首次从 pool 里获取:", p)p.Name = "first"fmt.Printf("设置 p.Name = %s\n", p.Name)pool.Put(p)fmt.Println("p.name:", p.Name) // p仍然有效p1 := pool.Get().(*Person)fmt.Println("p1.name:", p1.Name)fmt.Println("Pool 没有对象了,调用 Get: ", pool.Get().(*Person))
}

输出

Creating a new Person
首次从 pool 里获取: &{}
设置 p.Name = first
p.name: first
p1.name: first
Creating a new Person
Pool 没有对象了,调用 Get:  &{}

从这个例子可以看出,当p被存入pool后,p仍然是有效的,并且p的字段并没有因为p存入pool而被清空

sync.pool底层实现

sync.pool结构体

type Pool struct {noCopy noCopy// 每个 P 的本地队列,实际类型为 [P]poolLocallocal     unsafe.Pointer // local fixed-size per-P pool, actual type is [P]poolLocal// [P]poolLocal的大小localSize uintptr        // size of the local arrayvictim     unsafe.Pointer // local from previous cyclevictimSize uintptr        // size of victims array// 自定义的对象创建回调函数,当 pool 中无可用对象时会调用此函数New func() interface{}
}

上文提到,sync.pool是lock-free的,关键就在于这里的local是一个[GOMAXPROCS]poolLocal数组,由于每个 P 都有自己的一个本地对象池 poolLocal,Get 和 Put 操作都会优先存取本地对象池。由于 P 的特性,操作本地对象池的时候整个并发问题就简化了很多,可以尽量避免并发冲突

poolLocal定义如下

type poolLocal struct {poolLocalInternal// 将 poolLocal 补齐至两个缓存行的倍数,防止 false sharing,// 每个缓存行具有 64 bytes,即 512 bit// 目前我们的处理器一般拥有 32 * 1024 / 64 = 512 条缓存行// 伪共享,仅占位用,防止在 cache line 上分配多个 poolLocalInternalpad [128 - unsafe.Sizeof(poolLocalInternal{})%128]byte
}// Local per-P Pool appendix.
type poolLocalInternal struct {// P 的私有缓存区,使用时无需要加锁private interface{}// 公共缓存区。本地 P 可以 pushHead/popHead;其他 P 则只能 popTailshared  poolChain
}

poolChain 是一个双端队列的实现

type poolChain struct {// 只有生产者会 push to,不用加锁(Get不会触发对象窃取)head *poolChainElt// 读写需要原子控制。 pop fromtail *poolChainElt
}type poolChainElt struct {poolDequeue// next 被 producer 写,consumer 读。所以只会从 nil 变成 non-nil// prev 被 consumer 写,producer 读。所以只会从 non-nil 变成 nilnext, prev *poolChainElt
}type poolDequeue struct {// The head index is stored in the most-significant bits so// that we can atomically add to it and the overflow is// harmless.// headTail 包含一个 32 位的 head 和一个 32 位的 tail 指针。这两个值都和 len(vals)-1 取模过。// tail 是队列中最老的数据,head 指向下一个将要填充的 slot// slots 的有效范围是 [tail, head),由 consumers 持有。headTail uint64// vals 是一个存储 interface{} 的环形队列,它的 size 必须是 2 的幂// 如果 slot 为空,则 vals[i].typ 为空;否则,非空。// 一个 slot 在这时宣告无效:tail 不指向它了,vals[i].typ 为 nil// 由 consumer 设置成 nil,由 producer 读vals []eface
}

sync.pool Get接口调用时,可能会触发对象窃取,poolDeque可能被多个P访问,sync.pool内部使用CAS原子操作保证并发安全性

slab分配器

参考:https://segmentfault.com/a/1190000043626203

为什么需要slab分配器

linux伙伴系统管理内存的最小单位是物理内存页page,如果为几十个或者几百个字节的分配请求分配一整个页,将造成大量内存

slab分配器在内核代码中使用

对于用户空间,glibc的malloc内部维护一个空闲链表

使用slab分配器的好处

缓存友好

利用 CPU 高速缓存提高访问速度。当一个对象被直接释放回 slab 对象池中的时候,这个内核对象还是“热的”,仍然会驻留在 CPU 高速缓存中。如果这时,内核继续向 slab 对象池申请对象,slab 对象池会优先把这个刚刚释放 “热的” 对象分配给内核使用,因为对象很大概率仍然驻留在 CPU 高速缓存中,所以内核访问起来速度会更快

弥补伙伴系统调用路径长和只能按照整个物理页分配的缺点

伙伴系统只能分配 2 的次幂个完整的物理内存页,这会引起占用高速缓存以及 TLB 的空间较大,导致一些不重要的数据驻留在 CPU 高速缓存中占用宝贵的缓存空间,而重要的数据却被置换到内存中。 slab 对象池针对小内存分配场景,可以有效的避免这一点

slab分配器使用场景

主要是分配如下结构体对象的场景

  • struct task_struct

  • struct mm_struct

  • struct fd

  • struct page

  • struct socket

对于每种结构体,内核会创建一个专属的slab分配器

slab底层原理

暂时只需要了解slab是什么,和slab的基本概念。前面的区域,后面再探索吧~

相关文章:

从golang的sync.pool到linux的slab分配器

最近学习golang的时候,看到golang并发编程中有一个sync.pool,即对象池,猛地一看这不跟linux的slab分配器类似嘛,赶紧学习记录下 这里先总结下设计sync.pool和slab的目的 sync.pool 为了缓解特定类型的对象频繁创建和销毁&#x…...

Python分形几何可视化—— 复数迭代、L系统与生物分形模拟

Python分形几何可视化—— 复数迭代、L系统与生物分形模拟 本节将深入探索分形几何的奇妙世界,实现Mandelbrot集生成器和L系统分形树工具,并通过肺部血管分形案例展示分形在医学领域的应用。我们将使用Python的NumPy进行高效计算,结合Matplo…...

【超详细】英伟达Jetson Orin NX-YOLOv8配置与TensorRT测试

文章主要内容如下: 1、基础运行环境配置 2、Torch-GPU安装 3、ultralytics环境配置 4、Onnx及TensorRT导出详解 5、YOLOv8推理耗时分析 基础库版本:jetpack5.1.3, torch-gpu2.1.0, torchvision0.16.0, ultralytics8.3.146 设备的软件开发包基础信息 需…...

Go语言学习-->项目中引用第三方库方式

Go语言学习–>项目中引用第三方库方式 1 执行 go mod tidy 分析引入的依赖有没有正常放在go.mod里面 找到依赖的包会自动下载到本地 并添加在go.mod里面 执行结果: 2 执行go get XXXX(库的名字)...

Vue Fragment vs React Fragment

文章目录 前言🧩 一、概念对比:Vue Fragment vs React Fragment📦 二、使用示例对比✅ Vue 3 中使用 Fragment✅ React 中使用 Fragment 🔍 三、差异解析1. **使用方式**2. **传递属性(如 key)**3. **插槽系…...

【LRU】 (最近最少使用)

LRU (最近最少使用) 文章目录 LRU (最近最少使用)一、LRU是什么?二、实现1.常规算法2.双栈更替总结 一、LRU是什么? LRU(Least Recently Used)是一种常见的缓存淘汰策略,核心思想是 “淘汰最长时间未被使用的缓存数据…...

每日Prompt:云朵猫

提示词 仰视,城镇的天空,一片形似猫咪的云朵,用黑色的简笔画,勾勒出猫咪的形状,可爱,俏皮,极简...

AI浪潮下的IT行业:威胁、转变与共生之道

目录 前言1 AI在IT行业的具体应用场景1.1 软件开发中的AI助手1.2 运维与监控的智能化1.3 测试自动化与质量保障1.4 安全防护中的智能威胁识别 2 AI对IT从业者的实际影响2.1 工作内容的结构性变化2.2 技能结构的再平衡 3 IT从业者不可替代的能力与价值3.1 复杂系统的架构与抽象能…...

基于功能基团的3D分子生成扩散模型 - D3FG 评测

D3FG 是一个在口袋中基于功能团的3D分子生成扩散模型。与通常分子生成模型直接生成分子坐标和原子类型不同,D3FG 将分子分解为两类组成部分:官能团和连接体,然后使用扩散生成模型学习这些组成部分的类型和几何分布。 一、背景介绍 D3FG 来源…...

Python Cookbook-7.12 在 SQLite 中储存 BLOB

任务 想将 BLOB 存入一个 SQLite 数据库, 解决方案 Python的 PySQLite 扩展提供了 sqlite.encode 函数,它可帮助你在 SOLite 数据库中插入二进制串。可以基于这个函数编写一个小巧的适配器类: import sqlite,cPickle class Blob(object):自动转换二进制串def __init__(self…...

蓝耘服务器与DeepSeek的结合:引领智能化时代的新突破

🌟 嗨,我是Lethehong!🌟 🌍 立志在坚不欲说,成功在久不在速🌍 🚀 欢迎关注:👍点赞⬆️留言收藏🚀 🍀欢迎使用:小智初学…...

无人机光纤FC接口模块技术分析

运行方式 1. 信号转换:在遥控器端,模块接收来自遥控器主控板的电信号。 2.电光转换:模块内部的激光发射器将电信号转换成特定波长的光信号。 3.光纤传输:光信号通过光纤跳线传输。光纤利用全内反射原理将光信号约束在纤芯内进行…...

【LeetCode】3170. 删除星号以后字典序最小的字符串(贪心 | 优先队列)

LeetCode 3170. 删除星号以后字典序最小的字符串(中等) 题目描述解题思路java代码 题目描述 题目链接:3170. 删除星号以后字典序最小的字符串 给你一个字符串 s 。它可能包含任意数量的 * 字符。你的任务是删除所有的 * 字符。 当字符串还…...

Oracle 用户名大小写控制

Oracle 用户名大小写控制 在 Oracle 数据库中,用户名的默认大小写行为和精确控制方法如下: 一 默认用户名大小写行为 不引用的用户名:自动转换为大写 CREATE USER white IDENTIFIED BY oracle123; -- 实际创建的用户名是 "WHITE"…...

作为过来人,浅谈一下高考、考研、读博

写在前面 由于本人正在读博,标题中的三个阶段都经历过或正在经历,本意是闲聊,也算是给将要经历的读者们做个参考、排雷。本文写于2022年,时效性略有落后,不过逻辑上还是值得大家参考,若所述存在偏颇&#…...

立志成为一名优秀测试开发工程师(第十一天)—Postman动态参数/变量、文件上传、断言策略、批量执行及CSV/JSON数据驱动测试

目录 一、Postman接口关联与正则表达式应用 1.正则表达式解析 2.提取鉴权码。 二、Postman内置动态参数以及自定义动态参数 1.常见内置动态参数: 2.自定义动态参数: 3.“编辑”接口练习 三、图片上传 1.文件的上传 2.上传后内容的验证 四、po…...

Global Security Market知识点总结:主经纪商业务

在全球证券市场的复杂体系中,主经纪商业务(Prime Brokerage)占据着独特且关键的位置。这一业务为大型机构投资者提供了一系列至关重要的服务,极大地影响着金融市场的运作与发展。 一、主经纪商业务的定义 主经纪商业务是投资银行…...

算法练习-回溯

今天开始新的章节,关于算法中回溯法的练习,这部分题目的难度还是比较大的,但是十分锻炼人的思维与思考能力。 处理这类题目首先要注意几个基本点: 1.关于递归出口的设置,这是十分关键的,要避免死循环的产…...

AI代码助手需求说明书架构

AI代码助手需求说明书架构 #mermaid-svg-6dtAzH7HjD5rehlu {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-6dtAzH7HjD5rehlu .error-icon{fill:#552222;}#mermaid-svg-6dtAzH7HjD5rehlu .error-text{fill:#552222;s…...

PTC过流保护器件工作原理及选型方法

PTC过流保护器件 ‌(Positive Temperature Coefficient,正温度系数热敏电阻)是一种过流保护元件,其工作原理基于电阻值随温度变化的特性。当电路正常工作时,PTC的阻值很小,电流可以顺畅通过;但当…...

RabbitMQ 在解决数据库高并发问题中的定位和核心机制

RabbitMQ 在解决数据库高并发问题中的定位和核心机制 它是间接但极其有效的解决方案,以下内容聚焦如何最大化发挥 RabbitMQ 的潜力: 一、核心机制落地强化方案 1. 精准的异步化切割 关键原则:区分 “必须同步” 和 “可异步” 操作 #merma…...

VSCode主题定制:CSS个性化你的编程世界

在今天的数字世界,编程环境已成为开发者的第二大脑,而主题正是个性化你的创意空间的关键。本文将指导你如何使用CSS自定义VSCode的主题,让你的IDE不仅功能强大,更具视觉个性。 思路分析 设计思路: 创建主色调基调和…...

Windows 下彻底删除 VsCode

彻底删除 VS Code (Visual Studio Code) 意味着不仅要卸载应用程序本身,还要删除所有相关的配置文件、用户数据、插件和缓存。这可以确保你有一个完全干净的状态,方便你重新安装或只是彻底移除它。 重要提示: 在执行以下操作之前&#xff0c…...

一文带你入门Java Stream流,太强了,mysqldba面试题及答案

list.add(“世界加油”); list.add(“世界加油”); long count list.stream().distinct().count(); System.out.println(count); distinct() 方法是一个中间操作(去重),它会返回一个新的流(没有共同元素)。 Stre…...

FastAPI安全异常处理:从401到422的奇妙冒险

title: FastAPI安全异常处理:从401到422的奇妙冒险 date: 2025/06/05 21:06:31 updated: 2025/06/05 21:06:31 author: cmdragon excerpt: FastAPI安全异常处理核心原理与实践包括认证失败的标准HTTP响应规范、令牌异常的特殊场景处理以及完整示例代码。HTTP状态码选择原则…...

阿里云 RDS mysql 5.7 怎么 添加白名单 并链接数据库

阿里云 RDS mysql 5.7 怎么 添加白名单 并链接数据库 最近帮朋友 完成一些运维工作 ,这里记录一下。 文章目录 阿里云 RDS mysql 5.7 怎么 添加白名单 并链接数据库最近帮朋友 完成一些运维工作 ,这里记录一下。 阿里云 RDS MySQL 5.7 添加白名单1. 登录…...

《Brief Bioinform》: 鼠脑单细胞与Stereo-seq数据整合算法评估

一、写在前面 基因捕获效率、分辨率一直是空间转录组细胞类型识别的拦路虎,许多算法能够整合单细胞(single-cell, sc)或单细胞核(single-nuclear, sn)数据与空间转录组数据,从而帮助空转数据的细胞类型注释。此前我们介绍过近年新出炉的Stereo-seq平台&…...

基于Springboot的宠物领养系统

本系统是一个面向社会的宠物领养平台,旨在帮助流浪宠物找到新家庭,方便用户在线浏览、申请领养宠物,并支持管理员高效管理宠物、公告和用户信息。 技术栈: -后端: Java 8Spring BootSpring MVCMyBatis-PlusMySQL 8R…...

AI API、AI 聊天助手,两大服务助力应用智能化转型

网络效应、转换成本——这些一度定义了我们这个时代商业逻辑的规则,在 AI 时代迅速崩塌。创新性功能被无差别克隆包围,差异化优势在底层能力翻新中消散…… 更别说那些决策迟缓、行动无法言出法随的“后来者”,注定与市场窗口擦身而过。唯快…...

Windows 下搭建 Zephyr 开发环境

1. 系统要求 操作系统:Windows 10/11(64位)磁盘空间:至少 8GB 可用空间(Zephyr 及其工具链较大)权限:管理员权限(部分工具需要) 2. 安装必要工具 winget安装依赖工具&am…...