malloc()和free()的原理及实现
在C语言中只能通过malloc()和其派生的函数进行动态的申请内存,而实现的根本是通过系统调用实现的(在linux下是通过sbrk()系统调用实现)。
malloc()到底从哪里得到了内存空间?答案是从堆里面获得空间。也就是说函数返回的指针是指向堆里面的一块内存。操作系统中有一个记录空闲内存地址的链表。当操作系统收到程序的申请时,就会遍历该链表,然后就寻找第一个空间大于所申请空间的堆结点,然后就将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。
malloc()在运行期动态分配分配内存,free()释放由其分配的内存。malloc()在分配用户传入的大小的时候,还分配的一个相关的用于管理的额外内存,不过,用户是看不到的。所以,
实际的大小 = 管理空间 + 用户空间
在64位系统中,malloc(0)的有效内存大小为24,32位中为12,准确的说是至少是这么多,并且这些内存是可以用的
结果为
此外,堆中的内存块总是成块分配的,并不是申请多少字节,就拿出多少个字节的内存来提供使用。堆中内存块的大小通常与内存对齐有关(8Byte(for 32bit system)或16Byte(for 64bit system)。
因此,在64位系统下,当(申请内存大小+sizeof(struct mem_control_block) )% 16 == 0的时候,刚好可以完成一次满额的分配,但是当其!=0的时候,就会多分配内存块。
在linux系统下面一个程序的堆的管理是通过内存块进行管理的,也就是将堆分成了很多大小不一的内存块。这些块怎么管理尼,比如怎么查询块的大小,怎么查询块是否正在被程序使用,怎么知道这个块的地址。为了解决内存块的管理所以要设计一个管理内存块的数据结构,详细的数据结构如下:
/**内存控制块数据结构,用于管理所有的内存块
* is_available: 标志着该块是否可用。1表示可用,0表示不可用
* size: 该块的大小
**/
struct mem_control_block {
int is_available;
int size;
};
有了管理内存块的数据结构,那么在内存中堆的组织形式也好理解了,也就是堆是由很多内存块组成的,所以有了如下的示意图:
综合上面的知识,可以很容易想到malloc()实现的大体思路。首先挨个检查堆中的内存是否可用,如果可用那么大小是否能满足需求,要是都满足的话就直接用。当遍历了堆中的所有内存块时,要是没有能满足需求的块时就只能通过系统调用向操作系统申请新的内存,然后将新的内存添加到堆中。思路很简单,malloc()实现流程图如下所示:
看完上面的思路,也会很容易的想到free()函数的实现思路,只要将内存管理块设置为可用就可以了。这样下次调用malloc()函数的时候就可以将该内存块作为可分配块再次进行分配了。
最后,贴上malloc()和free()实现的代码:
malloc()实现:
/**内存控制块数据结构,用于管理所有的内存块
* is_available: 标志着该块是否可用。1表示可用,0表示不可用
* size: 该块的大小
**/
struct mem_control_block {
int is_available;
int size;
}; /**在实现malloc时要用到linux下的全局变量
*managed_memory_start:该指针指向进程的堆底,也就是堆中的第一个内存块
*last_valid_address:该指针指向进程的堆顶,也就是堆中最后一个内存块的末地址
**/
void *managed_memory_start;
void *last_valid_address; /**malloc()功能是动态的分配一块满足参数要求的内存块
*numbytes:该参数表明要申请多大的内存空间
*返回值:函数执行结束后将返回满足参数要求的内存块首地址,要是没有分配成功则返回NULL
**/
void *malloc(size_t numbytes) {
//游标,指向当前的内存块
void *current_location;
//保存当前内存块的内存控制结构
struct mem_control_block *current_location_mcb;
//保存满足条件的内存块的地址用于函数返回
void *memory_location;
memory_location = NULL;
//计算内存块的实际大小,也就是函数参数指定的大小+内存控制块的大小
numbytes = numbytes + sizeof(struct mem_control_block);
//利用全局变量得到堆中的第一个内存块的地址
current_location = managed_memory_start; //对堆中的内存块进行遍历,找合适的内存块
while (current_location != last_valid_address) //检查是否遍历到堆顶了
{
//取得当前内存块的内存控制结构
current_location_mcb = (struct mem_control_block*)current_location;
//判断该块是否可用
if (current_location_mcb->is_available)
//检查该块大小是否满足
if (current_location_mcb->size >= numbytes)
{
//满足的块将其标志为不可用
current_location_mcb->is_available = 0;
//得到该块的地址,结束遍历
memory_location = current_location;
break;
}
//取得下一个内存块
current_location = current_location + current_location_mcb->size;
} //在堆中已有的内存块中没有找到满足条件的内存块时执行下面的函数
if (!memory_location)
{
//向操作系统申请新的内存块
if (sbrk(numbytes) == -1)
return NULL;//申请失败,说明系统没有可用内存
memory_location = last_valid_address;
last_valid_address = last_valid_address + numbytes;
current_location_mcb = (struct mem_control_block)memory_location;
current_location_mcb->is_available = 0;
current_location_mcb->size = numbytes;
}
//到此已经得到所要的内存块,现在要做的是越过内存控制块返回内存块的首地址
memory_location = memory_location + sizeof(struct mem_control_block);
return memory_location;
}
free()实现:
/**free()功能是将参数指向的内存块进行释放
*firstbyte:要释放的内存块首地址
*返回值:空
**/
void free(void *firstbyte)
{
struct mem_control_block *mcb;
//取得该块的内存控制块的首地址
mcb = firstbyte - sizeof(struct mem_control_block);
//将该块标志设为可用
mcb->is_available = ;
return;
}
参考:
https://blog.csdn.net/c1s2p3/article/details/50522185
https://blog.csdn.net/qq_29350001/article/details/70213602
https://www.cnblogs.com/debuging/p/3158147.html
malloc()和free()的原理及实现的更多相关文章
- 浅谈malloc()和free()工作原理
编程之路刚刚开始,错误难免,希望大家能够指出. malloc()和free()是我经常需要用到的函数,一般情况下,C程序使用malloc()在堆上分配内存,free()释放内存,两者的参数和返回值就 ...
- malloc 底层实现及原理
摘要:偶尔看到面试题会问到 malloc 的底层原理,今天就来记录一下,毕竟学习要“知其所以然”,这样才会胸有成竹. 注:下面分析均是基于 linux 环境下的 malloc 实现.步骤是:先总结结论 ...
- C语言malloc和free实现原理
以下是一段简单的C代码,malloc和free到底做了什么? int main() { char* p = (char*)malloc(32); free(p); return 0; } malloc ...
- malloc的内存分配原理
0 堆内存的在计算机内存中的形式 根据<The C Programming language>推测得到堆内存,图中的Heap区域即为堆内存块(Heap区域的数目不代表计算机堆内存的真实数目 ...
- C++ new和delete实现原理——new和delete最终调用malloc和free
new和delete最终调用malloc和free,关于malloc和free实现原理参见这篇文章: http://blog.csdn.net/passion_wu128/article/detail ...
- malloc实现机制
使用过c语言的都知道malloc是一个动态分配内存的函数,还可以通过free释放内存空间. 如果我们想分析一下malloc的源码,这其实不是一会就能看懂的,但是我们可以讨论一下malloc的简单实现. ...
- iOS微信内存监控
WeTest 导读 目前iOS主流的内存监控工具是Instruments的Allocations,但只能用于开发阶段.本文介绍如何实现离线化的内存监控工具,用于App上线后发现内存问题. FOOM(F ...
- 内存优化总结:ptmalloc、tcmalloc和jemalloc(转)
转载于:http://www.cnhalo.net/2016/06/13/memory-optimize/ 概述 需求 系统的物理内存是有限的,而对内存的需求是变化的, 程序的动态性越强,内存管理就越 ...
- 微信团队原创分享:iOS版微信的内存监控系统技术实践
本文来自微信开发团队yangyang的技术分享. 一.前言 FOOM(Foreground Out Of Memory),是指App在前台因消耗内存过多引起系统强杀.对用户而言,表现跟crash一样. ...
随机推荐
- python 时间差计算
import time import datetime datebg=input("date begin:") dateed=input("date end:" ...
- Matlab从入门到精通 Chapter5 数据可视化--
5-1 图形绘制示例 >> x2=-17:0.02:3; >> y2=1./((x2+3).^2+1)+1./((x2+9).^2+4)+5; >> subplot ...
- 洛谷P4016 负载平衡问题 费用流
这道题还是很好的. 考察了选手对网络流的理解. 首先,任意两个相邻点之间的运货量时没有限制的. 我们可以将相邻点之间的流量建为无限大,单位费用设为 1,代表运输一个货物需耗费一个代价. 由于题目要求最 ...
- luogu P3604 美好的每一天(莫队+二进制)
这个题还是可以的. 但是卡常卡得我心力憔悴.还是太菜了 我们把一个区间当做一个26位二进制数,每一位代表一个英文,二进制数的每一个位0代表这一位对应的字母出现了偶数次,否则出现了奇数次. 那么一个区间 ...
- [SHOI2009]Booking 会场预约
题目:洛谷P2161. 题目大意:有一些操作,分为两种: A.增加一个从第l天到第r天的预约,并删除与这个预约冲突的其他预约,输出删除了多少个预约. B.输出当前有效预约个数. 两个预约冲突定义为两个 ...
- BZOJ 4896 [Thusc2016]补退选 (Trie树维护vector)
题目大意:略 这竟然是$thusc$的题... 先把询问里加入的串全拎出来,建出$Trie$树,$Trie$里每个节点都开一个$vector$记录操作标号,再记录操作数量$sum$ 然后瞎**搞搞就行 ...
- 关于iptables允许samba的问题
今天同事跟我说他们部门的共享不能用了,想了想,最近变更的只有iptables,于是看看是否是这个原因,发现没有允许samba的入站和出站规则,我的iptables规则默认是所有都drop的,但是不知确 ...
- python购物车系统
购物车系统模拟:product_list = [ ('java',100), ('python',200), ('键盘',500), ('电脑',4000), ('mac Book',7000),]S ...
- Python 语言中经常有疑惑的地方
*)关于for循环中range(2),i到底是从0还是1开始.特别是在用数组的长度作为range的参数的时候经常会犯糊涂 #首先 >>> for i in range(5): ... ...
- 紫书 习题8-19 UVa 1312 (枚举技巧)
这道题参考了https://www.cnblogs.com/20143605--pcx/p/4889518.html 这道题就是枚举矩形的宽, 然后从宽再来枚举高. 具体是这样的, 先把所有点的高度已 ...