malloc函数的底层实现你是否清楚
malloc函数的底层实现你是否清楚
说起malloc函数,每个人都能说出它的功能,而且我们经常会用到,那么今天我要说的是关于malloc函数在编译器的底层实现,如果你对它的实现已经很清楚了,那么你可以不往下看了,因为这篇博客只是就它的一些简单原理进行了整理,你可以等我的下一篇博客,对它的深层的一些函数在进行的一些讲述。
这篇博客对于深层的函数实现并没有解释,只是让我们明白了windows系统中的一些分配算法的原理。请读者多多指正,因为在空闲链表分配算法上我看到过不同的说法。
关于VirtualAlloc函数:
首先我们来看一下VirtualAlloc函数,它是WINDOWS系统提供给我们的一个API,它的功能就是向操作系统给我们批发内存。为什么叫批发呢,因为在操作系统中并不是我们所编写的应用程序直接向系统申请内存,在VirtualAlloc函数向内存申请内存的时候是申请内存的大小必须是4096字节的整数倍,这就相当于是一个水果贩子去大批发市场进货,人家批发市场是有起发的要求的,不是说你去买上几斤都行,所以我们都是从水果贩子的手上再去买水果。这样是有道理的,因为你一个程序假如只需要申请几字节的内存,那么系统给我们4096字节的大小是不是很浪费。
那么用VirtualAlloc申请的内存又是怎么拿给我们的程序来用的呢,在这里就牵扯到了分配算法。其实在windows中在对管理器上提供了几个函数用来创建、分配、释放和销毁堆空间(我们申请的空间是在堆上的),从而实现了分配算法。
HeapCreat:创建一个堆
HeapAlloc:在堆里分配空间
HeapFree:释放分配的内存
HeapDestroy:销毁一个堆
这里的HeapCreat便是创建一个堆空间它申请内存空间便是通过VirtualAlloc函数来实现的。HeapAlloc函数是在堆空间里面给用户分配小的内存空间,如果内存不足它也能通过VirtualAlloc向系统申请更多的内存。
Malloc函数:
说了前面那么多,终于说道我们的重点了,malloc函数其实是上面几个Heap操作函数的包装。
堆空间是程序申请出来的一大块空间,当我们用malloc函数申请空间的时候大小是不确定的,有可能是很小的一块空间也有可能是很大的一块空间,所以对堆空间也是需要一定的方法进行管理的,当你需要申请空间的时候按照你申请的大小以一定方法分配给你。
这就是分配算法,分配算法有很多种,对于不同的场合是有不同的分配算法的。在这里只简单的描述一下几种简单的算法。
1. 空闲链表
我们知道在一块堆空间中,能用的空间到时候是一块一块的分布着,空闲链表的方法就是把这些空闲的块用链表的方式进行连接,如果用户申请一块空间,就可以遍历这个空闲链表找到能够容纳你申请的大小的一个空闲块,然后拆分,把合适大小的一块返回给你;当用户释放一块空间时,在把这块空间链接在空闲链表上。下面来看一下它的结构:

这里画的是其中的一块,在这块空间的前面存放了两个指针,N代表next指针用来指向它的下一块空间,如果下一块为空,则指针指向NULL,P代表Prev指针,用来指向它的前一块空间,如果为NULL代表他就是空闲链表的第一块空间。
这里给出了一个只有三个块的空闲链表,它们的指针指向就是如图所指那样,此时我的块都是空闲的。假如我现在要申请一块空间,它是怎么实现的呢?

假设我要申请的空间大小刚好是第二块空间的大小,那么会把第二块空间的地址返回给我们,然后把这块空间从原来的空闲链表上删掉。此时链表如下图所示:

其实一般找到的空间都是比我们所申请的空间大的,然后把这块空间进行了拆分,一部分就是就是我们所申请的空间,另一部分为剩下的空间,它还是对应在原来的空心链表上。
这样就实现了一种简单的分配算法,其实在释放这块空间的时候,虽然知道指向这块空间的指针,但是堆并不知道这块空间的大小,那么它就不知道该释放多大的一块空间。所以其实在我们刚才说的分配算法里,在给用户分配空间的时候会多分配4个字节,作用就是用来存放这块空间的大小,这样的话在释放的时候找到这一块空间,就知道这块空间到底有多大,然后进行释放。
但是,我们知道有内存越界这种情况,就是在我们用内存的时候不小心用了它后面不属于它的空间,那么像空闲链表这种结构,可能就会把那块地方存放的两个指针进行了修改,这样不就破坏了我们的链表,然后整个空间就不能再使用了,这就是这种结构的缺点。
2. 位图
还有一种分配方式是位图,它是把我们的一块堆空间进行划分,划分成大小形同的一些块,当用户申请空间时,它会给我们分配整数个块以至于能存放你所申请的大小,第一个块是头,接下来的块都是用户申请的空间的主体,这样的话在位图中,我们的每一个快都有可能且只有可能有三种情况,就是头(Head)/主体(Body)/空闲(Free)。
下图就是一个例子,在下图中分配了两片内存,第一片占用了四个块,第二片占用的五个块。

这样做的话到底有什么好处呢,首先这样分配的话,对于这个堆的存储信息,它是记录在一个数组里。因为每一个小块是有三种可能状态,那么用二进制的两个位就能够表示了,假如设为00位空闲,10位主体,11为头。这样整个位图在一个数组中就能表示了。
这样每一个堆空间都可以用N个字节来表示,比如堆的大小为1Mb,分成1M/128=8000个块(每个块设为128字节),我们知道一个int是4个字节32位,那么能用8000/(32/2)=512个int来存储,所以一个大小为512int的数组就是一个完整的位图。
位图有它的优点:
比空闲链表更加稳定,因为可以对数组进行备份,而且就算某个块损坏,也不会影响整个位图其他的块空间。
速度比较快还容易管理。
同时也有它的缺点:
也容易造成块的浪费,因为毕竟它是整数倍的分配。
当堆比较大的时候,可能这个位图会很大,数组很庞大,可能效率也并不像想象那么快。
3. 对象池
还有一种方法是对象池,也是把堆空间分成了大小相等的一些块,它是认为某些场合每次分配的空间都相等,所以每次就直接返回一个块的大小,它的管理方法可以是链表也可以是位图。因为不用每次查找合适的大小的内存返回,所以效率很高。
其实在实际的应用中,堆的分配算法有很多,上面的三种只是其中简单的几种,而且在实际分配中它是根据应用场景可能对应不同的分配算法,也可能是多种算法的结合。只有这样才会达到高效的这么一个原则。
malloc函数的底层实现你是否清楚的更多相关文章
- malloc 函数工作机制(转)
malloc()工作机制 malloc函数的实质体现在,它有一个将可用的内存块连接为一个长长的列表的所谓空闲链表.调用malloc函数时,它沿连接表寻找一个大到足以满足用户请求所需要的内存块.然后,将 ...
- C语言 malloc函数
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. ...
- 关于malloc函数的动态分配问题
malloc函数动态分配了一个整型的内存空间,让abc都指向刚申请的空间,所以只有最后一个赋值语句的值保留在了空间里 #include<stdio.h> main() { int *a,* ...
- malloc函数详解
一.原型:extern void *malloc(unsigned int num_bytes); 头文件:#include <malloc.h> 或 #include <alloc ...
- malloc 函数到底做了什么?
请看下面的代码. 猜测结果是什么?编译通过吗? #include <stdio.h> #include <stdlib.h> int main() { ; char *ptr ...
- malloc函数和其他内存分配函数
1. 需要包含头文件:#i nclude 或 #i nclude 函数声明(函数原型): void *malloc(int size); 说明:malloc 向系统申请分配指定size个字节的内存空间 ...
- malloc函数
C语言中,使用malloc函数向内存中动态申请空间. 函数的原型是extern void *malloc(unsigned int num_bytes); 可见,函数返回的是指针类型,参数是要申请的空 ...
- 如何实现一个malloc函数
一.概述 1.malloc简介 函数所在头文件:<stdlib.h> 函数原型是:void *malloc (size_t n) 函数功能:在内存的动态存储区中分配一个长度为size的连续 ...
- C语言malloc()函数:动态分配内存空间
头文件:#include <stdlib.h> malloc() 函数用来动态地分配内存空间(如果你不了解动态内存分配,请查看:C语言动态内存分配及变量存储类别),其原型为:void* m ...
随机推荐
- POJ 3208-Apocalypse Someday(数位dp)
题意:给定n,输出第n大包含666的数字. 分析:dp[i][j][k][l]表示 长度为i,当前位是否是6,前一位是否6,是否已经包含666,表示的数量,再用二分找出第n大的这样的数字. #incl ...
- 开源了一个iOS输入控件【原】
1.Github 地址:https://github.com/linyc/InputBarFollowKeyboard 2.说明文档:https://github.com/linyc/InputBar ...
- LeetCode题解——Reverse Integer
题目: 数字翻转,即输入123,返回321:输入-123,返回-321. 代码: class Solution { public: int reverse(int x) { , sign = ; ) ...
- HDU 4714 Tree2cycle
Tree2cycle dfs 不是根节点:如果边数大于等于2,则删除与父节点的边.并且是一条环,那么每个点的度数是2,则还要删除num(每个节点儿子数)-2,只留两个儿子.当然删除边的儿子也要连到环上 ...
- 【转】Bash中的shopt选项
set选项与shopt选项是两组不同的内容,用set -o和shopt -p可以分别查看两个组所有的打开和关闭的条目, 在默认状态下,有些是打开的,有些是关闭的,shopt各选项随着bash版本的更新 ...
- POJ 3764 (异或+字典树)
早就听过用字典树求异或最大值,然而没做过.发现一碰到异或的题就GG,而且因为以前做过的一道类似的题(事实上并不类似)限制了思路,蠢啊= =. 题意:一棵带权的树,求任意两点间路径异或的最大值. 题解: ...
- UITableView性能优化
关于UITableView的性能优化,网络上也有一些总结.在这里就介绍下我们项目中遇到的问题以及对应的解决方法.相信我们遇到的问题也有一定的普适性,能够作为其他问题的优化方案. Instruments ...
- hdu 2212
1.简单的思维问题 各个位上阶乘的和 要和这个数相匹配 这样才能得到正确的解.各个位上阶乘的和 是最大是9*9!这样来求解.999999999 9个9 最大的各个位上的阶乘的和为3265920=9 ...
- ulua学习笔记(二):官方资料及问题解决方案
uLua&SimpleFramework入门视频教程网盘地址 视频教程地址 http://pan.baidu.com/s/1gd8fG4N游戏框架地址 https://github.com/j ...
- PowerDesigner概念数据模型 CDM
目标:本文主要介绍PowerDesigner中概念数据模型 CDM的基本概念. 一.概念数据模型概述数据模型是现实世界中数据特征的抽象.数据模型应该满足三个方面的要求:1)能够比较真实地模拟现实世界2 ...