jemalloc 5.3.0源码总结

请添加图片描述
注意:jemalloc 的最新版本里没有所谓的 huge class,bin 中slab外面也不再套一个run的概念了,看其它人分享的文章时需要注意。

简述

用户侧通过 tcache 来访问,tcache 是一个线程的申请又释放的对象的缓存,它绑定了一个固定的arena(分配大小超过8m的除外,8m以上会选择huge arena 来分配),有一个gc机制负责将不用的object 归还给 arena。
arena 分配对象时以8k为界限,以上均为大对象,以下均为小对象。小对象是按 slab 去管理的,即每次从 huge page 或 page 分配一个大的 extent 作为 slab,划分后将整个slab挂在bin的对应大小的组上,然后标记一个返回给用户;大对象则直接分配一个extent 放在arena 的large list上。
arena 是一个逻辑的概念,它真实的数据分配是在shard上进行的,各arena 有不同的shard,内存之间不相交,shard 有 hpa(huge page)和 pac(page)两种分配器,优先使用hpa来分配,但hpa只能承担最多一个huge page 大小的内存,再大就只能用pac去分配。
hpa 在分配前会先用base分配器申请128 个大页,每次分配时从某个大页上切一块下来,pac每次缓存的内存不够时会通过base分配器来分配
base 分配器一次分配一个block,并存在base 的 list 上。
pac,hpa,base都有一个类型的缓存结构来维护退回来的内存(ppset_t,ecache_t,edata_avail_t)。它们都是用bitmap 维护一个bin列表,bin中的每个元素是符合这一组内存大小的pairing heap最小堆。

size 与 bin 中 index 的映射

每个bin index对应的大小可以直接查表。
从size 到bin index 的关系在4k以下是可以查表的,4k以上需要计算
group = size / total_group / min_size
group 内的 delta = 1…(size / (total_group + 1))个0 & (size -1)去掉group的位
具体可以参考(sz_size2index_compute)
请添加图片描述
每个size class 的具体情况可以跑这个代码看下

scs = [None] * 1024class SC:def __init__(self, lg_max_lookup, lg_page, lg_ngroup, index, lg_base, lg_delta, ndelta) -> None:self.index = indexself.lg_base = lg_baseself.lg_delta = lg_deltaself.ndelta = ndeltaself.unit_size =  (1 << self.lg_base) + (ndelta << self.lg_delta)# should from os, default 4096self.page_size = (1 << lg_page)# small or largeself.is_small =  (self.unit_size < (1 << (lg_page + lg_ngroup)))self.pages = self.get_slab_size() if self.is_small else 0# small or largeself.lg_delta_lookup = lg_delta if (self.unit_size <= (1 << lg_max_lookup))  else 0 def get_slab_size(self):# at most lg_group * 2 - 1 timesuse_size = self.page_sizewhile (use_size % self.unit_size) != 0:use_size += self.page_sizereturn use_sizedef size_classes(lg_ptr_size, lg_quantum, lg_tiny_min, lg_max_lookup, lg_page, lg_ngroup):lg_base = lg_tiny_minlg_delta = lg_basengroup = 1 << lg_ngroupptr_bits = (1 << lg_ptr_size) * 8index = 0ndelta = 0nlbins = 0 # max lookup table sizenbins = 0ntiny = 0lg_tiny_maxclass = 0# tiny size is 2^n enlargingwhile lg_base < lg_quantum:sc = SC(index, lg_base, lg_delta, ndelta, lg_page, lg_ngroup, lg_max_lookup)scs[index] = scif sc.lg_delta_lookup:nlbins = index + 1if sc.is_small:nbins += 1ntiny += 1lg_tiny_maxclass = lg_baseindex += 1lg_delta = lg_baselg_base += 1if ntiny != 0:# first non tiny sc# use base/delta = quatum-1 rather than base = quatum and delta = 0lg_base -= 1first_non_tiny_sc = SC(lg_max_lookup, lg_page, lg_ngroup, index, lg_base, lg_delta, 1)scs[index] = first_non_tiny_sclg_base += 1index += 1lg_delta += 1if sc.is_small:nbins += 1while ndelta < ngroup:sc = SC(lg_max_lookup, lg_page, lg_ngroup, index, lg_base, lg_delta, ndelta)scs[index] = scindex += 1ndelta += 1if sc.is_small:nbins += 1# other groupslg_base = lg_base + lg_ngroupwhile lg_base < ptr_bits - 1:ndelta = 1ndelta_limit = ngroup - 1 if lg_base == (ptr_bits - 2) else ngroupwhile ndelta <= ndelta_limit:sc = SC(lg_max_lookup, lg_page, lg_ngroup, index, lg_base, lg_delta, ndelta)scs[index] = scif sc.lg_delta_lookup:nlbins = index + 1lookup_maxclass = (1 << lg_base) + (ndelta << lg_delta)if sc.is_small:nbins += 1small_maxclass = (1 << lg_base) + (ndelta << lg_delta)lg_large_minclass = lg_base + 1 if lg_ngroup > 0 else lg_base + 2large_maxclass = (1 << lg_base) + (ndelta << lg_delta)print("large_maxclass:", lg_base, ndelta, lg_delta, large_maxclass)index += 1ndelta += 1lg_base += 1lg_delta += 1print(f"""ntiny: {ntiny}
nlbins: {nlbins}
nbins: {nbins}
n: {index}
lg_tiny_maxclass: {lg_tiny_maxclass}
lookup_maxclass: {lookup_maxclass}
small_maxclass: {small_maxclass}
lg_large_minclass: {lg_large_minclass}
large_minclass: {1 << lg_large_minclass}
large_maxclass: {large_maxclass}""")indexs = []bases = []deltas = []unit_sizes = []is_small = []pages = []print("""index\tbase\tdelta\tunit_size\tis_small\tpages""")for i in range(1,1024):if scs[i] is None:breaksc = scs[i]indexs.append(i)bases.append(sc.lg_base)deltas.append(sc.ndelta << sc.lg_delta)unit_sizes.append(sc.unit_size)is_small.append("yes" if sc.is_small else "no")pages.append(sc.pages)print(f"""{i}\t{1 << sc.lg_base}\t{sc.ndelta << sc.lg_delta}\t{sc.unit_size}\t{"yes" if sc.is_small else "no"}\t{sc.pages}""")LG_SIZEOF_PTR = 3
LG_QUANTUM = 4
SC_LG_TINY_MIN = 3
SC_LG_MAX_LOOKUP = 12 
LG_PAGE = 12
SC_LG_NGROUP = 2
size_classes(LG_SIZEOF_PTR, LG_QUANTUM, SC_LG_TINY_MIN, SC_LG_MAX_LOOKUP, LG_PAGE, SC_LG_NGROUP)SC_NTINY = (LG_QUANTUM - SC_LG_TINY_MIN)
SC_NGROUP = (1 << SC_LG_NGROUP)
SC_NPSEUDO = SC_NGROUP
SC_LG_FIRST_REGULAR_BASE = (LG_QUANTUM + SC_LG_NGROUP)
SC_NBINS = SC_NTINY + SC_NPSEUDO + SC_NGROUP * (LG_PAGE + SC_LG_NGROUP - SC_LG_FIRST_REGULAR_BASE)
print("SC_NBINS is :", SC_NBINS)

cache 的 gc

tcache 大概是这样的:
bound --------full---------waterline----empty
|------------------|----------------|-------------|
其中当数量超过full时,触发一次flush,将一半的对象还回去,剩余的数量称为waterline。(除此之外好像在flush后,又分配对象,然后又释放对象,重回到waterline 以后,比waterline 多的对象作为detach 的对象也可以被flush 走,但没看太懂,不太确定)

大小对象的分配与释放

无论大小对象都是先从tcache分配,检查bitmap 看对应大小的对象有没有剩余,如果有直接拿,如果没有才去arena 分配。

小对象(<8k)

小对象分配是在arena 的 bin 上,每个大小的bin都对应着一系列的slab。分配时先从cur_slab分配,如果cur_slab没有了,就取一个nonfull list的slab放在cur_slab上 分配,如果nonfull list 也没有了,就去pac或hpa重新分配slab,挂在cur_slab上分配。当cur_slab分配没了,就会挂在full slab上,在释放时发现它在full slab 上,则放回到nonfull slab 上。分配的指针与 arena 还有 size 的映射存在arena_emap_global 上。
请添加图片描述

大对象(>8k)

直接走 pac 或 hpa 分配和释放。分配后挂在arena->large_list 上。有arena_emap_global来维护指针到large_list上edata 元素的映射。释放时也是直接从 large list 上拿走。

internal 分配器

arena 有两种:普通+huge。大于 8m(oversize_threshold) 的对象是在 huge arena 上分配的,小于 8m 的在自己绑定的普通 arena 上分配(除非是用户指定了arena,这时才不会自动转到 huge arena 上分配)。
无论哪种分配器,在分配时都会先尝试用 hpa(hpa 最多 hold 一个大页以下的内存,一般linux 配置是2m),如果超出 hpa 的能力才去普通 pac 分配。

hpa 分配器

hpa 在第一次分配时会申请 128 个 hugepage放在empty list 中。分配时先从 psset 缓存找对应大小的 bitmap,看有没有空闲,如果有就从这个大小的pairing heap上拿最小空闲的一个huge page 下来(这个最小空闲指在这个huge page 上最大的一段连续内存的大小,在所有同组huge page 中是最小的
),如果没有空闲就找下一个更大大小的组,直到找到一个 huge page。根据 huge page的bitmap找到对应的位置,并更新这个 huge page 上最大的没分配范围。
分配出来的内存由edata来记录,并放在shard->emap 上,这是一个前缀树,可以在释放时根据前缀树找到是否有相邻的释放内存,并对它们做合并。
分配后的 huge page 会根据剩余的最大内存重新挂回到psset 上。
释放时内存先从emap 上摘下来,然后在如果它的回归能够让huge page 的最大空闲更新,则把huge page 插入到更合适的 bin 中。释放时如果发现一页完全空了,则可以添加回到psset->empty list中。这些做完后还要标记这个范围到psset->to_purge,会在合适的时间触发purge(实际是提示操作系统不再使用madvice(NONEED))
请添加图片描述

pac 分配器

pac 也有类似的缓存,但过程不全相同。pac 的页合并不像huge page 那样用bitmap,而是借助 shard_map (见上图) 。pac 在分配时会先找完全满足的bin去分配,如果配置的bin没有空闲,它会找更大一点的bin来split,但只会向上找最多opt_lg_extent_max_active_fit个bin。如果新分配出来的内存对extent 做了切割,那么切割剩的extent 会加入到retained 缓存中。
pac 有三个缓存,dirty / muzzy / retained。分配时先去 dirty cache 找匹配的或能切割的,再去 muzzy 找,如果都找不到,会去 retained 中找,retained 中找不到时才真正去 base 分配,并在切割后将剩余的放到retained 中。
之所以新分配的内存还是有切割可能,是因为pac 每次去 base 进货的单位内存是不断增加的,进货的单位内存有一个 pac->exp_grow 来维护(它有一个增长到的最大 limit值,但应该不起作用,因为limit 值设置为了最大object class size,而这个值对于64位的指针来说就是2^63。)
muzzy 与 dirty 内存的区别: muzzy 的 lazy purge 的时间会更久,dirty 内存在 purge 时,会检查一下可否转为 muzzy,如果能转,则它 purge 的时间会更 delay。这样可以避免频繁向操作系统申请和释放。

base 分配器

base 真正分配 block 时也像 pac 一样有一个申请内存的单位不断增长的机制(由 base->pind_last记录上一次分配的大小级别,下次分配只能比这个级别相等或更大)
真实的分配是由sbrk(dss方式) 或 mmap 系统调用来分配的,默认是先用 mmap 分配(DSS_DEFAULT =“secondary”),用户可以调整。
dss 比 mmap 快,但没有 mmap 灵活。

bitmap

为了加速查找,bitmap 采用的是多层的形式,对当于一个bit-skip-list。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/72445.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Jenkins 持续集成:Linux 系统 两台机器互相免密登录

背景知识 我们把public key放在远程系统合适的位置&#xff0c;然后从本地开始进行ssh连接。 此时&#xff0c;远程的sshd会产生一个随机数并用我们产生的public key进行加密后发给本地&#xff0c;本地会用private key进行解密并把这个随机数发回给远程系统。 最后&#xf…

RHCA之路---EX280(6)

RHCA之路—EX280(6) 1. 题目 Create an application greeter in the project samples which uses the Docker image registry.lab.example.com/openshift/hello-openshift so that it is reachable at the following address only: https://greeter.apps.lab.example.com (Not…

分布式 - 服务器Nginx:基础系列之Nginx静态资源配置优化sendfile | tcp_nopush | tcp_nodelay

文章目录 1. sendfile 指令2. tcp_nopush 指令3. tcp_nodelay 指令 1. sendfile 指令 请求静态资源的过程&#xff1a;客户端通过网络接口向服务端发送请求&#xff0c;操作系统将这些客户端的请求传递给服务器端应用程序&#xff0c;服务器端应用程序会处理这些请求&#xff…

PaddleOCR训练部署文档

Cuda安装 wget https://developer.download.nvidia.com/compute/cuda/11.6.0/local_installers/cuda_11.6.0_510.39.01_linux.run sh cuda_11.6.0_510.39.01_linux.run#可能会报错&#xff0c;查看/var/log/nvidia-installer.log &#xff0c;kill -9 [ID]可以解决vim ~/.bash…

Linux:tomcat (源码包安装)(官网下载-安装-启动-配置-等等等-----从入门到入土)

介绍 Apache Tomcat软件是一个开源实现 Jakarta Servlet、Jakarta Server Pages、Jakarta Expression Language、Jakarta WebSocket、Jakarta Annotations 和 Jakarta Authentication 规范。 这些规范是Jakarta EE平台的一部分。 Apache Tomcat软件是在开放和参与式中开发的。 …

基于SSM的学校运动会信息管理系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

网络协议从入门到底层原理学习(一)—— 简介及基本概念

文章目录 网络协议从入门到底层原理学习&#xff08;一&#xff09;—— 简介及基本概念一、简介1、网络协议的定义2、网络协议组成要素3、广泛的网络协议类型网络通信协议网络安全协议网络管理协议 4、网络协议模型对比图 二、基本概念1、网络互连模型2、计算机之间的通信基础…

洞察商机,驱动创新:智能数据分析引领企业发展

“五度易链”产业大数据解决方案由产业经济、智慧招商、企业服务、数据服务四大应用解决方案组成&#xff0c;囊括了产业经济监测、产业诊断分析、企业监测预警、企业综合评估、大数据精准招商、招商智能管理、企业管理、企业培育、企业市场服务、企业金融服务、产业数据开放服…

智慧工地可视化解决方案-智慧工地源码

智慧工地是指运用信息化手段&#xff0c;围绕施工过程管理&#xff0c;建立互联协同、智能生产、科学管理的施工项目信息化生态圈&#xff0c;并将此数据在虚拟现实环境下与物联网采集到的工程信息进行数据挖掘分析&#xff0c;提供过程趋势预测及专家预案&#xff0c;实现工程…

每日一题 2651. 计算列车到站时间

难度&#xff1a;简单 不多说 class Solution:def findDelayedArrivalTime(self, arrivalTime: int, delayedTime: int) -> int:return (arrivalTime delayedTime) % 24

为什么在线客服系统的消息撤回功能是有必要的?

如今在日常工作和沟通中&#xff0c;很多企业都在使用在线客服系统跟客户进行线上交流和协作。然而有时候客服可能会不小心发送错误的消息或包含敏感信息的消息&#xff0c;人们在现实的沟通交流中是不会真实存在“说出去的话还能收回来”的情况&#xff0c;但这是在网络上&…

2023-09-05 LeetCode每日一题(从两个数字数组里生成最小数字)

2023-09-05每日一题 一、题目编号 2605. 从两个数字数组里生成最小数字二、题目链接 点击跳转到题目位置 三、题目描述 给你两个只包含 1 到 9 之间数字的数组 nums1 和 nums2 &#xff0c;每个数组中的元素 互不相同 &#xff0c;请你返回 最小 的数字&#xff0c;两个数…

大模型综述论文笔记6-15

这里写自定义目录标题 KeywordsBackgroud for LLMsTechnical Evolution of GPT-series ModelsResearch of OpenAI on LLMs can be roughly divided into the following stagesEarly ExplorationsCapacity LeapCapacity EnhancementThe Milestones of Language Models Resources…

如何解决前端传递数据给后端时精度丢失问题

解决精度丢失 有时候我们在进行修改操作时&#xff0c;发现修改既不报错也不生效。我们进行排查后发现服务器端将数据返回给前端时没有出错&#xff0c;但是前端js将数据进行处理时却出错了&#xff0c;因为id是Long类型的&#xff0c;而js在处理后端返回给前端的Long类型数据…

git 给分支添加描述

需求:分支多了不知道当前分支的用处可以使用git br用来描述 效果: 全局安装命令 npm i -g git-br 项目内使用 git br 给f-230825-4-zhou分支备注 git config branch.f-230825-4-zhou.description 用来开发第四迭代需求 再次git br查看效果

【PHP代码审计】反序列化漏洞实战

文章目录 概述资源下载地址Typecho代码审计-漏洞原理call_user_func()_applyFilter()、get()与__get__toString()__construct()install.php POC利用漏洞利用复现利用链执行phpinfo()GET利用POST利用 getshell生成payload漏洞利用蚁剑连接 总结 概述 序列化&#xff0c;“将对象…

13 mysql date/time/datetime/year 的数据存储

前言 这里主要是 由于之前的一个 datetime 存储的时间 导致的问题的衍生出来的探究 探究的主要内容为 int 类类型的存储, 浮点类类型的存储, char 类类型的存储, blob 类类型的存储, enum/json/set/bit 类类型的存储 本文主要 的相关内容是 datetime/date/time/year 类类型…

NS2安装及入门实例——(ns2.35 / Ubuntu20.04)

文章目录 一、ns2安装1、更新系统源2、准备工作3、下载安装包4、安装5、问题① 问题1② 问题2③ 问题3 6、安装成功7、环境配置 二、nam安装1、安装2、问题 三、实例 一、ns2安装 1、更新系统源 sudo apt-get update sudo apt-get upgrade2、准备工作 sudo apt-get install …

Oracle 遍历变量游标

背景 由于我们的数据库系统中的游标特别多&#xff0c;DBA让我们优化&#xff0c;减少游标的使用。 电脑系统&#xff1a;windows数据库&#xff1a;Oracle数据库图形化界面工具&#xff1a;Toad&#xff0c;DBeaver(我測試的時候用的)记录日期&#xff1a;2023-09-04 具体实…

计算机重点学科评级B-,山东省属重点高校考情分析

山东科技大学(B-) 考研难度&#xff08;☆☆&#xff09; 内容&#xff1a;23考情概况&#xff08;拟录取和复试分析&#xff09;、院校概况、23专业目录、23复试详情、各专业考情分析、各科目考情分析。 正文1175字预计阅读&#xff1a;3分钟 2023考情概况 山东科技大学计…