百度工程师带你探秘C++内存管理

news2024/11/16 16:46:18

一、概述

ptmalloc是开源GNU C Library(glibc)默认的内存管理器,当前大部分Linux服务端程序使用的是ptmalloc提供的malloc/free系列函数,而它在性能上远差于Meta的jemalloc和Google的tcmalloc。服务端程序调用ptmalloc提供的malloc/free函数申请和释放内存,ptmalloc提供对内存的集中管理,以尽可能达到:

  • 用户申请和释放内存更加高效,避免多线程申请内存并发和加锁

  • 寻求与操作系统交互过程中内存占用和malloc/free性能消耗的平衡点,降低内存碎片化,不频繁调用系统调用函数

简单概括ptmalloc的内存管理策略:

  • 预先向操作系统申请并持有一块内存供用户malloc,同时管理已使用和空闲的内存

  • 用户执行free,会将回收的内存管理起来,并执行管理策略决定是否交还给操作系统

接下来,将从ptmalloc数据结构、内存分配及优缺点介绍最经典的c++内存管理器的实现和使用(以32位机为例)。

二、内存管理

2.1 数据结构

为了解决多线程锁争夺问题,将内存分配区分为主分配区(main_area)和非主分配区(no_main_area)。同时,为了便于管理内存,对预申请的内存采用边界标记法划分成很多块(chunk);ptmalloc内存分配器中,malloc_chunk是基本组织单元,用于管理不同类型的chunk,功能和大小相近的chunk串联成链表,被称为一个bin。

main_arena与non_main_arena

主分配区和非主分配区形成一个环形链表进行管理, 每一个分配区利用互斥锁实现线程对该分配区的访问互斥。每个进程只有一个主分配区,但允许有多个非主分配区,且非主分配区的数量只增加不减少。主分配区可以访问进程的heap区域和mmap映射区域,即主分配区可以使用sbrk()和mmap()分配内存;非主分配区只能使用mmap()分配内存。

对于不同arena的管理策略大致如下:

  • 分配内存

    • 查看该线程的私有变量中是否已经存在一个分配区并对其进行加锁操作,如果加锁成功,则使用该分配区分配内存;如果未找到该分区或加锁失败,遍历环形链表中获取一个未加锁的分配区

    • 如果整个环形链表中没有未加锁的分配区,开辟一个新的分配区,将其加入循环链表并加锁,使用该分配区满足当前线程的内存分配

  • 释放内存

    • 先获取待释放内存块所在的分配区的锁,如果有其他线程正在使用该分配区,等待其他线程释放该分配区互斥锁后,再释放内存

主分配区和非主分配区的结构如下:

其中fastbinsY和bins是对实际内存块的管理和操作结构:

  • fastbinsY: 用以保存fast bins

  • bins[NBINS * 2 - 2]: unsorted bin(1个,bin[1])、small bins(62 个,bin[2]~bin[63])、large bins(63 个,bin[64]~bin[126])的集合,一共有 126 个表项(NBINS = 128),bin[0] 和 bin[127] 没有被使用

malloc_chunk与bins

ptmalloc统一管理heap和mmap映射区域中空闲的chunk,当用户进行分配请求时,会先试图在空闲的chunk中查找和分割,从而避免频繁的系统调用,降低内存分配的开销。为了更好的管理和查找空闲chunk,在预分配的空间的前后添加了必要的控制信息,内存管理结构malloc_chunk的成员及作用如下:

  • mchunk_prev_size: 前一个空闲chunk的大小

  • mchunk_size: 当前chunk的大小

  • 必要的属性标志位:

    • 前一个chunk在使用中(P = 1)

    • 当前chunk是mmap映射区域分配(M = 1)或是heap区域分配(M = 0)

    • 当前chunk属于非主分配区(A = 0)或非主分配区(A = 1)

  • fd和bk: chunk块空闲时存在,用于将空闲chunk块加入到空闲chunk块链表中统一管理

基于chunk的大小和使用方法,划分出以下几种bins:

  • fast bins

    fast bins仅保存很小的堆,采用单链表串联,增删chunk都发生在链表的头部,进一步提高小内存的分配效率。fast bins记录着大小以8字节递增的bin链表,一般不会和其他堆块合并。

  • unsorted bin

    small bins和large bins的缓冲区,用于加快分配的速度,chunk大小无尺寸限制,用户释放的堆块,会先进入unsorted bin。分配堆块时,会优先检查unsorted bin链表中是否存在合适的堆块,并进行切割并返回。

  • small bins

    保存大小 < 512B的chunk的bin被称为small bins。small bins每个bin之间相差8个字节,同一个small bin中的chunk具有相同大小,采用双向循环链表串联。

  • large bins

    保存大小 >= 512B的chunk的bin被称为large bins。large bins中的每一个bin分别包含了一个给定范围内的chunk,其中的chunk按大小降序,相同大小按时间降序。

当然,并不是所有chunk都按上述的方式来组织,其他常用的chunk,如:

  • top chunk: 分配区的顶部空闲内存,当bins不能满足内存分配要求的时候,会尝试在top chunk分配。

    • 当top chunk > 用户请求大小,top chunk会分为两个部分:用户请求大小(user chunk)和剩余top chunk大小(remainder chunk)

    • 当top chunk < 用户所请求大小,top chunk就通过sbrk(main_arena)或mmap(non_main_arena)系统调用来扩容

2.2 内存分配与释放

概括内存malloc和free的流程大致如下:

内存分配malloc流程

1、获取分配区的锁

2、计算出需要分配的内存的chunk实际大小

3、如果chunk的大小 < max_fast,在fast bins上查找适合的chunk;如果不存在,转到5

4、如果chunk大小 < 512B,从small bins上去查找chunk,如果存在,分配结束

5、需要分配的是一块大的内存,或者small bins中找不到chunk:

a.遍历fast bins,合并相邻的chunk,并链接到unsorted bin中

b.遍历unsorted bin中的chunk:

   -能够切割chunk直接分配,分配结束

   -根据chunk的空间大小将其放入small bins或是large bins中,遍历完成后,转到6

6、需要分配的是一块大的内存,或者small bins和unsorted bin中都找不到合适的 chunk,且fast bins和unsorted bin中所有的chunk已清除:

从large bins中查找,反向遍历链表,直到找到第一个大小大于待分配的chunk进行切割,余下放入unsorted bin,分配结束

7、检索fast bins和bins没有找到合适的chunk,判断top chunk大小是否满足所需chunk的大小,从top chunk中分配

8、top chunk不能满足需求,需要扩大top chunk:

a.主分区上,如果分配的内存 < 分配阈值(默认128KB),使用brk()分配;如果分配的内存 > 分配阈值,使用mmap分配

b.非主分区上,使用mmap来分配一块内存

2、计算出需要分配的内存的chunk实际大小

3、如果chunk的大小 < max_fast,在fast bins上查找适合的chunk;如果不存在,转到5

4、如果chunk大小 < 512B,从small bins上去查找chunk,如果存在,分配结束

5、需要分配的是一块大的内存,或者small bins中找不到chunk:

a.遍历fast bins,合并相邻的chunk,并链接到unsorted bin中

b.遍历unsorted bin中的chunk:

   -能够切割chunk直接分配,分配结束

   -根据chunk的空间大小将其放入small bins或是large bins中,遍历完成后,转到6

6、需要分配的是一块大的内存,或者small bins和unsorted bin中都找不到合适的 chunk,且fast bins和unsorted bin中所有的chunk已清除:

从large bins中查找,反向遍历链表,直到找到第一个大小大于待分配的chunk进行切割,余下放入unsorted bin,分配结束

7、检索fast bins和bins没有找到合适的chunk,判断top chunk大小是否满足所需chunk的大小,从top chunk中分配

8、top chunk不能满足需求,需要扩大top chunk:

a.主分区上,如果分配的内存 < 分配阈值(默认128KB),使用brk()分配;如果分配的内存 > 分配阈值,使用mmap分配

b.非主分区上,使用mmap来分配一块内存

内存释放free流程

1、获取分配区的锁

2、如果free的是空指针,返回

3、如果当前chunk是mmap映射区域映射的内存,调用munmap()释放内存

4、如果chunk与top chunk相邻,直接与top chunk合并,转到8

5、如果chunk的大小 > max_fast,放入unsorted bin,并且检查是否有合并:

a.没有合并情况则free

b.有合并情况并且和top chunk相邻,转到8

6、如果chunk的大小 < max_fast,放入fast bin,并且检查是否有合并:

a.fast bin并没有改变chunk的状态,没有合并情况则free

b.有合并情况,转到7

7、在fast bin,如果相邻chunk空闲,则将这两个chunk合并,放入unsorted bin。如果合并后的大小 > 64KB,会触发进行fast bins的合并操作,fast bins中的chunk将被遍历合并,合并后的chunk会被放到unsorted bin中。合并后的chunk和top chunk相邻,则会合并到top chunk中,转到8

8.如果top chunk的大小 > mmap收缩阈值(默认为128KB),对于主分配区,会试图归还top chunk中的一部分给操作系统

三、优缺点

ptmalloc作为glibc默认的内存管理器,已经广泛的满足大多数大型项目的内存管理,同时它的实现思路也对后来的内存管理器提供了借鉴。

ptmalloc的介绍暂告一段落,接下来的几篇文章将继续探讨高性能内存管理库的集大成者——jemalloc、tcmalloc内存管理库。

更多好玩的小项目看我哔哩哔哩、Q裙:

小鱼快来啊的个人空间-小鱼快来啊个人主页-哔哩哔哩视频哔哩哔哩小鱼快来啊的个人空间,提供小鱼快来啊分享的视频、音频、文章、动态、收藏等内容,关注小鱼快来啊账号,第一时间了解UP注动态。编程学习群:725022484 每天分享一个编程小游戏~C/C++游戏源码素材及各种安装包, 私信不常看!https://space.bilibili.com/1827181878?spm_id_from=333.1007.0.0

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

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

相关文章

Datawhale组队学习:大数据 D2——分布式文件系统(HDFS)

妙趣横生大数据 Day2三、Hadoop 分布式文件系统(HDFS)1. 分布式文件系统2. HDFS 简介3. HDFS 体系结构4. HDFS存储原理数据冗余存储数据存储策略数据错误与恢复5. HDFS数据读写过程读写过程HDFS故障类型和其检测方法HDFS编程实验1. 本地和集群文件间操作2. 基本文件操作3. Hado…

Java基本语法【未完待续】

目录 一、注释方式 1、单行注释 // 2、多行注释 /*...*/ 3、文档注释 /**....*/ 二、标识符和关键字 三、数据类型 拓展及面试题讲解 1、整数拓展 进制 二进制0b 八进制0 十六进制0x 2、字符拓展 编码Unicode表 2字节 0~65536 3、字符串拓展 4、布尔值拓展 四、类型…

CleanMyMac X软件下载及详细功能介绍

mac平台的知名系统清理应用CleanMyMac在经历了一段时间的测试后&#xff0c;全新设计的X正式上线。与CleanMyMac3相比&#xff0c;新版本的UI设计焕然一新&#xff0c;采用了完全不同的风格。使用Windows电脑时&#xff0c;很多人会下载各类优化软件&#xff0c;而在Mac平台中&…

jsp高校教职工管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 jsp 高校教职工管理系统 是一套完善的web设计系统&#xff0c;对理解JSP java编程开发语言有帮助mvc模式 serlvetdaobean方式开发&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式 开发。开发环境为TOMCAT7.0,Myeclipse8.5开发&#…

tomcat安装和配置

目录 1.下载tomcat 2.解压tomcat压缩包 3.配置端口号 4.启动 命令行窗口日志乱码的解决 5.验证 tomcat 如果已经安装配置过jdk&#xff0c;则向下执行&#xff0c;若无&#xff0c;请先安装jdk。 1.下载tomcat 路径&#xff1a; Apache Tomcat - Apache Tomcat 9 Sof…

Windows 系统从零配置 Python 环境,安装CUDA、CUDNN、PyTorch 详细教程

文章目录1 配置 python 环境1.1 安装 Anaconda1.2 检查环境安装成功1.3 创建虚拟环境1.4 进入/退出 刚刚创建的环境1.5 其它操作1.5.1 查看电脑上所有已创建的环境1.5.2 删除已创建的环境2 安装 CUDA 和 CUDNN2.1 查看自己电脑支持的 CUDA 版本2.2 安装 CUDA2.3 安装 CUDNN2.4 …

LabVIEW中CPU和内存使用情况在NI分布式系统管理器中不可见

LabVIEW中CPU和内存使用情况在NI分布式系统管理器中不可见想使用NI分布式系统管理器监测网络连接实时控制器的CPU和内存使用情况。从左侧窗口的树中选择了感兴趣的实时目标&#xff0c;然后通过选择视图自动视图来确保启用自动查看。希望看到CPU/内存选项卡&#xff0c;但它有显…

算法导论【在线算法】—The Ski-Rental Problem、The Lost Cow Problem、The Secretary Problem

算法导论【在线算法】The Ski-Rental Problem问题描述在线算法证明The Lost Cow Problem问题描述在线算法类似问题—寻宝藏The Secretary Problem问题描述在线算法The Best Possible kThe Ski-Rental Problem 问题描述 假设你正在上滑雪课。每节课结束后&#xff0c;你决定&a…

【Element】el-table 表格

目录 ElementUI 表格分页&#xff08;每页20条&#xff09; 表格分页&#xff08;全部数据&#xff09; 表格排序&#xff08;全部数据&#xff09; 表格排序&#xff08;默认&#xff09; 两个el-table冲突 加载数据前显示“ 暂无数据 ” 表格项为路由 表头样式 树形…

Homebrew 安装遇到的问题

Homebrew 安装遇到的问题 例如&#xff1a;第一章 Python 机器学习入门之pandas的使用 文章目录Homebrew 安装遇到的问题前言一、安装二、遇到的问题1.提示 zsh: command not found: brew三、解决问题前言 使用 Homebrew 能够 安装 Apple&#xff08;或您的 Linux 系统&#…

React 合成事件理解

1 事件三个阶段 捕获、目标、处理 &#xff08;具体百度&#xff0c;后面有空补全&#xff09;2import React from "react";class Test extends React.Component {parentRef;childRef;constructor(props) {super(props);this.parentRef React.createRef();this.chil…

cmd 窗口、记事本打开后一片空白且几秒钟后闪退的问题解决方案汇总

前言 前段时间&#xff0c;电脑忽然出现了问题&#xff0c;首先是通过 微软应用商店 Microsoft Store 下载安装的 Snipaste 截图软件崩溃&#xff0c;不过将其卸载后&#xff0c;通过电脑管家下载后又可以正常使用了。 之后就是突然发现&#xff0c;记事本文本文档不能使用了…

分享112个HTML娱乐休闲模板,总有一款适合您

分享112个HTML娱乐休闲模板&#xff0c;总有一款适合您 112个HTML娱乐休闲模板下载链接&#xff1a;https://pan.baidu.com/s/15uBy1SVSckPPMM55fiudeQ?pwdkqfz 提取码&#xff1a;kqfz Python采集代码下载链接&#xff1a;采集代码.zip - 蓝奏云 Bootstrap视频网站模板 …

Terraform基础入门 (Infrastructure as Code)

文章目录前言介绍Terraform 术语Terraform 如何工作关于provider安装开启本地缓存demo1(dockernginx)demo2(dockerzookeeperkafka)参考资料前言 像写代码一样管理基础设施。 Terraform 使用较为高级的配置文件语法来描述基础设施&#xff0c;这个特性让你对配置文件进行版本化…

Ubuntu升级cmake

目录 1、下载cmake安装包 2、开始安装 3、查看cmake版本 参考链接&#xff1a; https://blog.csdn.net/qq_27350133/article/details/121994229 1、下载cmake安装包 cmake安装包下载&#xff1a;download | cmake 我们根据自身需求下载所需版本的cmake安装包&#xff0c;这…

万字干货 | 荔枝魔方基于云原生的架构设计与实践

近年来&#xff0c;荔枝集团在国内和海外的业务迅速发展&#xff0c;业务数据规模也是成几何式地增长&#xff0c;海量数据的计算分析场景、业务智能算法应用需求随之而生&#xff0c;为了快速地满足业务发展的需要&#xff0c;我们面临着诸多的技术挑战。技术挑战工程问题资源…

计算机如何思考与图灵完备

图灵完备是针对一套数据操作规则而言的概念,数据操作规则可以是一门编程语言,也可以是计算机实现里面的指令集,比如C/C++是图图灵完备的,通用CPU也是图灵完备的,但是GPU却不一定是图灵完备的。说白了图灵完备定义了一套规则,当这套规则可以实现图灵迹模型里的全部功能时,…

Unreal Engine09:自定义Pawn运动组件

写在前面 这里介绍一下如何为Pawn定制自定义的运动组件。 一、新建一个运动组件C类 需要在Editor中新建类&#xff0c;不要直接Visual Studio中新建&#xff1b;Pawn的运动组件继承自UPawnMovementComponent&#xff0c;需要勾选显示所有类才能找到&#xff1b; 初始时&…

【PowerQuery】PowerBI 的PowerQuery支持的数据集成

PowerBI中的各个Power组件已经被深度集成到PowerBI中,不再作为像Excel一样的独立组件而存在。在PowerBI的界面中为了快速导入这些常用的数据,也有相应的快速导入界面。PowerBI的快速导入界面位于主页面中,下图就是PowerBI的快速导入界面。 在PowerBI中的数据导入界面相比Exc…

Leetcode.958 二叉树的完全性检验

题目链接 Leetcode.958 二叉树的完全性检验 Rating : 1703 题目描述 给定一个二叉树的 root&#xff0c;确定它是否是一个 完全二叉树 。 在一个 完全二叉树 中&#xff0c;除了最后一个关卡外&#xff0c;所有关卡都是完全被填满的&#xff0c;并且最后一个关卡中的所有节点…