当前位置: 首页 > 编程日记 > 正文

为什么free()时不需要传指针大小

malloc()和free()是c中两个非常基本的函数,但这种最基本的东西往往都是特别复杂的。
malloc和free的原形如下:

void *malloc(unsigned int num_bytes);
void free(void *ptr);

在c的标准中并没有定义这两个函数的具体实现,在我们最常用的gcc中,malloc使用的是ptmalloc的实现,最早由Doug Lea完成,并被Wolfram Gloger改进以支持多线程。
malloc的具体实现过于复杂,这里就不具体展开了。
总体上说,ptmalloc的内存管理是基于内存池的,而它的内存来源有两种:
1 通过brk()获得
2 通过mmap()匿名映射获得
这两种获得内存的方式分别对应于进程地址空间的不同部分。64位linux进程的默认内存布局如下:


对于其中的Heap区域,就是我们通常意义上的堆空间,这部分内存将通过brk()获得。brk()的原理非常简单,只涉及指针的上下移动,也就是只分配虚拟地址空间,而不分配物理内存,当实际访问到此内存时,将触发page fault异常由操作系统完成物理内存的分配。由于操作简单,所以brk的效率非常高。
而另一个Memory Mapping Region区域,简称mmap区域,将通过操作系统的mmap()函数获得。mmap区域不仅可以提供内存的分配,还可以映射文件,比如通过mmap打开文件,或者加载so文件等等。
mmap函数的原形如下:

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); 

其中的flags,在使用mmap分配内存时,设置为MAP_ANONYMOUS,也就是匿名映射。当使用mmap()映射内存时,操作系统默认将对这块内存执行清0操作,因此相比于brk,mmap的效率相对较低。
基于内存池的内存管理,本质上都是把变长的内存分配转换为定长的内存分配。因为只有定长,才可以复用。
ptmalloc的内存池结构大致如下:


可以看出,内存的分配将根据实际的大小,选择定长块。比如请求的内存是23字节,那么将分配一块24字节的内存,而请求的如果是25字节,那么将分配一块32字节的内存(对于大于512字节的数据,统一存储在large bin中)。具体的分配策略就不展开了。
每一种大小的内存,都组成了一个一个的队列,在这些队列里维护了一个双向链表,将所有的小块内存串联起来。每一个小块内存(chunk)的结构如下:


上面的这个结构是一块在使用中的内存的状态。其中的mem部分,则是返回给用户的void*指针位置。而最开始的两个结构:size of previous chunk,和 size of chunk,则用于维护全局内存的链表。
之所以说是全局内存的链表,是由于内存分配时是先向系统请求一个比较大的内存块(64位系统一般为64Mb),之后从这64Mb内存中切出用户需要的大小分配给用户。而为了维护分配出去的内存块之间的关系,通过前两个结构来使所有内存块构成一个大的链表,当回收内存时,通过这个全局链表,将所有空闲内存组合起来,还给操作系统,其中有3个标记位:A,M,P,P标识前一块内存是否空闲。
下面的结构就是一块空闲内存的状态:

相比于使用中的状态,空闲部分的内存增加了4个新的结构(Forward pointer to next chunk in list 等,其中fd_nextsize和bk
_nextsize只存在于large bin中),这4个结构用于维护每个定长内存队列的双向链表结构,这个链表的存在主要是为了分配时查找内存时足够便利,可以基本上保证分配内存时的平均复杂度维持在O(1)。

在有了前面所有的介绍之后,可以总体上描述一下malloc和free的基本流程:
当用户向ptmalloc请求内存时:
1 首先查找定长内存分配池,如果查找到则返回
2 如果没有空闲内存可供使用,则向操作系统申请一块64Mb的内存,从中切出用户需要的内存,返回
当用户调用free释放内存时:
1 直接将内存放入适当的定长内存池队列
2 如果触发了一定的条件,则将所有空闲内存合并,如果满足释放条件,将内存全部还给操作系统

当然了,上面的描述中省略了太多的细节。比如什么时候走brk什么时候走mmap, 再比如当请求的内存大于一个阙值时,ptmalloc将会变成一个mmap的简单封装,还有触发内存归还操作系统的条件等等。
不过已经足够回答题目中的问题了:因为malloc的时候记录了大小。
这里还可以得出另一个结论:由于malloc的时候记录了大量的状态,所以在频繁使用malloc分配小内存时,会造成大量的内存浪费。举例来说,当反复malloc(1)时,每一次分配的内存在32字节:包括size of previous chunk,size of chunk,bk_chunk_pointer,fd_chunk_pointer共4个指针,合计4 * 8 = 32字节....



作者:littlersmall
链接:https://www.jianshu.com/p/871e63168033
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

相关文章:

redis cluster 安装配置

一、redis集群安装配置1、下载redis源码包并下载wget http://download.redis.io/releases/redis-3.0.7.tar.gz $ tar xzf redis-3.0.7.tar.gz $ cd redis-3.0.7 yum -y install gcc gcc-c libstdc-devel #解决相关依赖关系$ make && make install 因我们安装redis 集…

【ACM】汉诺塔

https://blog.csdn.net/xueerfei008/article/details/9904681

什么是机器人底盘 答案在这里!

机器人底盘承载了机器人本身的定位、导航及避障等基本功能,可帮助机器人实现智能行走,以思岚科技的ZEUS为例,内置SLAMWARE高性能自主定位导航模块,用户可根据实际需要搭载不同的应用,可广泛适用于餐厅、商场、银行、办…

嵌入式linux内存使用和性能优化

这本书有两个关切点:系统内存(用户层)和性能优化。 这本书和Brendan Gregg的《Systems Performance》相比,无论是技术层次还是更高的理论都有较大差距。但是这不影响,快速花点时间简单过一遍。 然后在对《Systems Performance》进行详细的学…

【算法导论】插入排序

循环不变式 在数学上阐述了通过循环(迭代,递归)去计算一个累计的目标值的正确性。 关于循环不变式,我们必须要证明三条性质: 初始化:循环第一次迭代之前,它为真。保持:如果循环的…

gdb+gdbserver

内容摘要 远程调试环境由宿主机GDB和目标机调试stub共同构成,两者通过串口或TCP连接。使用 GDB标准程串行协议协同工作,实现对目标机上的系统内核和上层应用的监控和调试功能。调试stub是嵌入式系统中的一段代码,作为宿主机GDB和目标机调试程…

Android开发技巧——去掉TextView中autolink的下划线

我们知道,在布局文件中设置textview的autolink及其类型,这时textivew上会显示link的颜色,并且文字下面会有一条下划线,表示可以点击。而在我们在点击textview时,应用将根据我们所设置的类型跳转到对应的界面。但是有时…

【算法导论】冒泡排序 选择排序

冒泡排序&#xff1a; //从大到小 void bubble_sort(int array[],int len) {int i,j,t;for(i0;i<len-1;i){for(j0;j<len-1-i;j){if(array[j]<array[j1]){tarray[j];array[j]array[j1];array[j1]t;} }} } 选择排序&#xff1a; //从大到小 void select_sort(int a…

监控平台zabbix高级配置

2019独角兽企业重金招聘Python工程师标准>>> 12月26日任务 19.12 添加自定义监控项目 19.13/19.14 配置邮件告警 19.15 测试告警 19.16 不发邮件的问题处理 添加自定义监控项目 zabbix可以自定义监控项目&#xff0c;满足个性化的需求。例如网站注册量、访问量等具体…

linux内存实际占用分析

作者: 黄永兵/译 出处:51CTO.com 阅读提示&#xff1a;本文是为那些经常疑惑的人准备的&#xff0c;“为什么一个简单的KDE文本编辑器要占用25M内存&#xff1f;”导致大多数人认为许多Linux应用程序&#xff0c;特别是KDE或GNOME程序都象ps报告一样臃肿...【51CTO.com独家译文…

CentOS 6.5 下Vim 配置图解

分享个CentOS 6.5 下Vim 配置图文详解&#xff0c;希望对大家有所帮助。 1. 登录并进入你常用的用户名下&#xff0c;查看其主目录 命令&#xff1a; # su xxx $ cd xxx $ ls -a 2.查看并建立目录和文件 首先看你的主目录~/ 下是否有.vimrc文件&#xff0c;没有就输入指令 $ to…

【ACM】杭电OJ 1106 函数atoi

函数atoi是把字符串转化成整数的函数&#xff0c;头文件为 #include "stdlib.h" e.g. 运行环境&#xff1a;Dev-C 5.11 杭电1106 调用了sort函数&#xff0c;运行的时间相对长一些。 #include "stdio.h" #include "string.h" #include "…

docker-dockerfile

docker文件存储驱动dockerfile镜像构建指令示例dockekr镜像是只读的&#xff0c;对容器修改的内容&#xff0c;一旦容器退出&#xff0c;所有的内容将会丢失。镜像是分层的&#xff0c;最上的一层为读写层&#xff08;写时复制和用时分配&#xff09; 文件系统存储驱动&#xf…

proc/[pid]/maps 文件解释

proc/[pid]/maps 文件解释 查看进程的虚拟地址空间是如何使用的。 该文件有6列&#xff0c;分别为&#xff1a; 地址&#xff1a;库在进程里地址范围 权限&#xff1a;虚拟内存的权限&#xff0c;r读&#xff0c;w写,x,s共享,p私有&#xff1b; 偏移量&#xff1a;库在进程里…

【ACM】UVa 1339

【题目】&#xff1a;给定两个长度相同且不超过100的字符串&#xff0c;判断是否能把其中一个字符串的各个字母重排&#xff0c;然后对26个字母做一一映射&#xff0c;使得两个字符串相同。输入两个字符串&#xff0c;输出“YES”或者“NO”。 【分析】&#xff1a;既然字母可…

springBoot PUT请求接收不了参数的解决办法

2019独角兽企业重金招聘Python工程师标准>>> 做项目的时候&#xff0c;想把接口写标准点&#xff0c;于是在更新内容的时候采用put提交内容&#xff0c;但是提交内容时总是获取不到参数&#xff0c;总是选择参数为null。 首先贴出我的put的方法控制器的代码 和之前的…

七牛云内容审核服务被选为「上海首批人工智能创新产品」

近日&#xff0c;上海人工智能应用场景建设实施计划正式发布&#xff0c;这是全国首次面向人工智能应用场景需求的征集计划。上海 10 大人工智能应用场景、19 个具体点位需求和 60 个人工智能创新产品集中首发&#xff0c;其中&#xff0c;上海七牛信息技术有限公司&#xff08…

linux动态库命名规则

说道“动态库版本兼容”&#xff0c;很多人头脑中首先蹦出的就是“Dll Hell”。啊&#xff0c;这曾经让人头疼的难题。时至今日&#xff0c;这个难题已经很好地解决了。 在进一步讨论之前来思考一个问题&#xff1a;Linux下为什么没有让人头痛的“DllHell”&#xff1f; 回答…

如何在同一系统里同时启动多个Tomcat

需要在同一系统里启动多个tomcat,应该怎么处理? tomcat是个服务程序&#xff0c;需要占用几个通讯端口&#xff0c;所以默认情况是不能启动多个tomcat,如果要启动多个tomcat,需要修改配置文件&#xff0c;通过在配置文件设置不同的通讯端口就可以做到.文件 %TOMCAT_HOME%/conf…

【ACM】Uva 455

【题目】&#xff1a;如果一个字符串可以由某个长度为k的字符串重复多次得到&#xff0c;则称该串以k为周期。输入一个长度不超过80的字符串&#xff0c;输出其最小正周期。 注意以下几点&#xff1a; 1、它的最小正周期一定可以被它的长度整除。 2第一个大循环下 i 可以等于…

前端自动化构建工具webpack (二)之css和插件加载总结

1. webpack只识别js文件&#xff0c;其他文件都需要转换成js文件。所有文件都是模块; 2. css解析 css需要css-loader ---》style-loader -----》less-loader less文件还需要less-loader &#xff08;注意书写顺序&#xff09; 3. plugins&#xff1a;他是一个数组&#…

使用command对象操作数据库

1.Command对象查询数据库 protected void Button1_Click(object sender, EventArgs e){//读取web.config节点配置string strcon ConfigurationManager.ConnectionStrings["testjm"].ConnectionString;//实例化sqlConnection对象SqlConnection con new SqlConnectio…

浅析C语言之uint8_t / uint16_t / uint32_t /uint64_t

一、C语言基本数据类型回顾 在C语言中有6种基本数据类型&#xff1a;short、int、long、float、double、char 1、数值类型 1&#xff09;整型&#xff1a;short、int、long 2&#xff09;浮点型&#xff1a;float、double 2、字符类型&#xff1a;char 二、typedef回顾 …

【ACM】UVa 489 刽子手游戏(自顶向下)

【题目】 Hangman Judge是一个猜英文单字的小游戏&#xff08;在电子字典中常会看到&#xff09;&#xff0c;游戏规则如下&#xff1a; 1、答案单字写在纸上&#xff08;每个字元一张纸&#xff09;&#xff0c;并且被盖起来&#xff0c;玩家每次猜一个英文字元&#xff08;le…

ssh远程执行多个命令

shell远程执行&#xff1a; 经常需要远程到其他节点上执行一些shell命令&#xff0c;如果分别ssh到每台主机上再去执行很麻烦&#xff0c;因此能有个集中管理的方式就好了。一下介绍两种shell命令远程执行的方法。 前提条件&#xff1a; 配置ssh免密码登陆 对于简单的命令&am…

【ACM】魔方矩阵

输出魔方矩阵 1、将1放在第一行中间一列&#xff1b; 2、从2开始直到nn止各数依次按下列规则存放&#xff1b;每一个数存放的行比前一个数的行数减1&#xff0c;列数加1&#xff1b; 3、如果上一个数的行数为1&#xff0c;则下一个数的行数为n&#xff08;指最下一行&#x…

iOS 秒数转换成时间,时,分,秒

//转换成时分秒 - (NSString *)timeFormatted:(int)totalSeconds{ int seconds totalSeconds % 60; int minutes (totalSeconds / 60) % 60; int hours totalSeconds / 3600; return [NSString stringWithFormat:"%02d:%02d:%02d",hours, minutes,…

charles和Fiddler感觉哪个更好用

1.fiddler还可以抓HTTPS的包&#xff0c;解析出来都可以2.charles更直观&#xff0c;可能是我先用charles的缘故。charles遍历一个站点&#xff0c;可以右键另存&#xff0c;保存全站文件资源。扒站首选&#xff0c; charles也可以抓https&#xff0c;我改游戏也是抓的https包

systemd用法

一、开机启动 对于那些支持 Systemd 的软件&#xff0c;安装的时候&#xff0c;会自动在/usr/lib/systemd/system目录添加一个配置文件。 如果你想让该软件开机启动&#xff0c;就执行下面的命令&#xff08;以httpd.service为例&#xff09;。 $ sudo systemctl enable http…