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函数的底层实现你是否清楚的更多相关文章

  1. malloc 函数工作机制(转)

    malloc()工作机制 malloc函数的实质体现在,它有一个将可用的内存块连接为一个长长的列表的所谓空闲链表.调用malloc函数时,它沿连接表寻找一个大到足以满足用户请求所需要的内存块.然后,将 ...

  2. C语言 malloc函数

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.                                                 ...

  3. 关于malloc函数的动态分配问题

    malloc函数动态分配了一个整型的内存空间,让abc都指向刚申请的空间,所以只有最后一个赋值语句的值保留在了空间里 #include<stdio.h> main() { int *a,* ...

  4. malloc函数详解

    一.原型:extern void *malloc(unsigned int num_bytes); 头文件:#include <malloc.h> 或 #include <alloc ...

  5. malloc 函数到底做了什么?

    请看下面的代码. 猜测结果是什么?编译通过吗? #include <stdio.h> #include <stdlib.h> int main() { ; char *ptr ...

  6. malloc函数和其他内存分配函数

    1. 需要包含头文件:#i nclude 或 #i nclude 函数声明(函数原型): void *malloc(int size); 说明:malloc 向系统申请分配指定size个字节的内存空间 ...

  7. malloc函数

    C语言中,使用malloc函数向内存中动态申请空间. 函数的原型是extern void *malloc(unsigned int num_bytes); 可见,函数返回的是指针类型,参数是要申请的空 ...

  8. 如何实现一个malloc函数

    一.概述 1.malloc简介 函数所在头文件:<stdlib.h> 函数原型是:void *malloc (size_t n) 函数功能:在内存的动态存储区中分配一个长度为size的连续 ...

  9. C语言malloc()函数:动态分配内存空间

    头文件:#include <stdlib.h> malloc() 函数用来动态地分配内存空间(如果你不了解动态内存分配,请查看:C语言动态内存分配及变量存储类别),其原型为:void* m ...

随机推荐

  1. codeforces 671B Robin Hood 二分

    题意:有n个人,每个人a[i]个物品,进行k次操作,每次都从最富有的人手里拿走一个物品给最穷的人 问k次操作以后,物品最多的人和物品最少的人相差几个物品 分析:如果次数足够多的话,最后的肯定在平均值上 ...

  2. windows内核窥探

    windows是一个非常优秀的OS,从今天开始,我要和大家共同分享windows给我们带来的快乐!本人只所以将自己的学习笔记与大家分享,一是让自己更深入的理解windows,再就是有什么疏漏之处,望大 ...

  3. java ee@ Myeclipse 2015 stable 1.0 完美破解方法

    Myeclipse 2015 stable 1.0 完美破解方法 破解步骤: 使用以前的注册机算号,版本选择Blue即可,后续可解锁Spring高级功能,即Bling的所有功能全部具备 1.1 进入m ...

  4. poj1848 Tree

    .....是我多想了. 我想开f[][0~3],看到百度上的题解都是[0~2]的,我就改了 方程不是特别难想.. f代表最小代价 f[i][0]是子树有环过i f[i][1]是子树除了i都成环了 f[ ...

  5. HDU-4689 Derangement DP

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4689 题意:初始序列1,2...n,求所有满足与初始序列规定大小的错排数目.. 这道题目感觉很不错~ ...

  6. hdu4435-charge-station(搜索+贪心)

    题意&题解: http://www.cnblogs.com/wuminye/p/3245546.html 说实话看了题解觉得很简单,但是比赛的时候真的是毫无头绪. 然而印象中做过一道类似的二进 ...

  7. Java TreeMap 源码解析

    继上篇文章介绍完了HashMap,这篇文章开始介绍Map系列另一个比较重要的类TreeMap. 大家也许能感觉到,网络上介绍HashMap的文章比较多,但是介绍TreeMap反而不那么多,这里面是有原 ...

  8. 统一样式的View应该用style修饰

    我们的应用中,常常有一些统一的组件,这时候应该用style来修饰.这样的话,修改起来也方便,代码也更简洁 比如,下面的代码,没有用style修饰 <LinearLayout xmlns:andr ...

  9. [OC Foundation框架 - 4] NSString的导出

      void exportString() { NSString *str = @"Hello, 坑爹"; NSString *path = @"/Users/hello ...

  10. 经验之巧妙的应用Map

    后台: @RequestMapping("/cmci/v_divide_check_add.do")    public String toDivideCheckAdd(HttpS ...