malloc与free(3)

这是我在CSDN上看到的一篇文章,有关malloc-free与new-delete的区别与联系,以及内存分配的介绍,也写的非常好。

原文:http://blog.csdn.net/happymawolf/article/details/6333114

看完这三篇文章,基本已经解决了我心中的疑惑了。

今天我想讨论下malloc-free和new-delete之间的区别与不同,深入探讨其内部实现的机制。如果你对此不屑一顾,请先不要那么自信,先来回答下面几个问题:
1、malloc和new、free和delete之间有什么区别?
2、为什么malloc时输入了所申请内存的大小size,但是free时怎么不用输入要释放内存的大小,而只要输入指向该内存的指针就可以了?其内部隐藏了什么处理机制?
3、同样new时需要输入要创建对象的个数,为什么delete释放时不需要输入对象个数?其处理和malloc-free又有什么不同?
   如果以上所有问题你都理解的明澈如水,那恭喜你,你对内存管理已经很有研究了,就请跳过本文!如果,还是一知半解或无知无解,那就请跟我一起进入我们的发现之旅!

   1、malloc-free的相关技术
   首先,我们先来看看malloc-free的函数定义,malloc有一个size_t类型的形参,用于输入所要申请内存的大小,并返回一个void*的指针,指向该块内存的起始位置;free则只有一个参数,就是所要释放内存的指针。
   void * __cdecl malloc(__in size_t _Size);
   void   __cdecl free(__inout_opt void * _Memory);
   可以以下结论:malloc和free是用于申请和释放原始内存(raw mem)。那为什么malloc申请内存时需要输入内存大小,而释放是则只需要该内存的指针就可以了呢?其实这与堆内存管理的内部机制有关。所有的内存的申请和释放都通过堆管理器来进行管理,而堆的管理有两种常用的实现方式:堆链表和位图。堆链表是把每次申请的内存块抽象成一个链表节点,而每个链表节点都有一个链表头header,header结构分别有一个指向上一块内存和下一个内存的指针,还有一个变量用于记录当前链表节点所对应的内存块的大小!此时你是否明白为啥free内存时不需要输入该内存的大小了?对了,就是因为它的大小已经记录在该块内存里了,一般情况header结构是存在内存的尾部,先别结题!此时,你没有啥想说的吗?你应该想到如果堆链表法进行堆管理有个缺点,就是每次都需要多申请sizeof(header)字节的内存用于记录堆链表节点信息,以空间换时间喽!位图法和堆链表法不一样,他没有什么节点,而是将内存按照固定大小的字节分割成小块,并建立一个位图表进行映射,表中每一位对应一块内存,如果未使用则标识为0,已被申请但不是所申请内存的最后一块内存则标识为1,是所申请内存的最后一块内存则标识为2。这样,在free时就依次读取标识,并置0,直到为2时,结束。但位图法也有缺点,关于内存块的划分大小,如果太大,容易造成碎片,如果太小,则导致位图表项很多,同样占用内存(此部分内容请参考操作系统相关书籍)。
  在此我在提一个扩展问题:当我们用free释放栈的指针时,会发生什么?如下面代码:
void main()
{
   int val;
   free(&val);
}
   千万别执行,可能会发生系统崩溃的!只能说执行结果未知,反正会破坏堆栈!但,深入想想,为啥会系统崩溃呢?大不了栈破损,程序执行错误呗!嘿嘿,这里就会用到上面所讲的堆链表的问题,自己想想吧!

  ok,我们讨论了malloc-free内存管理的内部机制,并解决了问题二的疑惑,好现在我们继续看看new-delete的实现。
   
  2、new-delete的相关技术
    首先,我们先讨论下new-delete和malloc-free的不同点:
    1> new-delete是操作符,而malloc-free是函数;
    2> new-delete 内部对malloc-free进行了封装,在申请内存的同时,扩展支持对class类对象的构造、析构函数的调用;
    而new-delete和malloc-free的相同点是他们底层的内存管理机制是一样的。因此,可以得出new-delete只是对malloc-free进行了封装,比malloc-free多做了一些事情,最主要的就是对类构造、析构函数的调用,以及一些类型安全的处理!而内存申请和释放部分与malloc-free是完全相同的。
    但因为new-delete申请和释放是类型相关的,所以其内部不仅关联到所申请内存的大小,还涉及到构造的类对象的个数。如代码new TYPE[num]。至于内存的大小我们已经明晰了,但是类对象的个数是否要记录呢,如果要记录,那存在哪里呢?可能现在有人会说不用记录啊,知道类对象的类型直接sizeof(TYPE),然后再用内存大小去除,不就可以了?答案是否定的。因为你申请的内存并不等于类对象的个数与类对象大小的乘积,它还存储额外的信息,如header,而且还要考虑到内存申请时的字节对齐。soso,无法通过内存大小计算,所以该类对象个数num是要记录的。也就是说new数组要记录两个数,这点是很关键的。那类对象个数num是记录在哪呢?是否也记录在内存里?no,不是,类对象的个数是有编译器编译过程中存储记录的(参考侯杰大师翻译的《effective c++》),而不是通过内存管理器。当你delete时,他会自动找打该指针对应的类对象的个数,循环调用析构函数,然后再free这块内存。
整个过程如下(仅是示意代码):
new  TYPE [num];
-----------------
{
p = malloc (size);
// 用new placemet 技术
p = new (buffer1) TYPE[num];
// 上句内部循环调用构造函数

return p;
}

delete p;
-----------------
{
void p1 = p;
// 循环调用析构函数
while (num--)
{
   p.~TYPE();
   p += sizeof(TYPE);
}
free(p1);
}

    以上代码是个人的理解,如果存在错误,就请各位大师批评指正!
    好了,所有的问题都以探讨完毕!“休息,休息!”O(∩_∩)O哈哈~

你可能感兴趣的:(header,delete,存储,扩展,编译器,操作系统相关)