网上有些帖子说关于malloc/free实现的细节,证实后发现不是那么回事。
这篇帖子前提是看过网上那篇关于说malloc实现的帖子:
http://blog.csdn.net/hzhzh007/article/details/6424638
首先那些帖子的说法是申请的每块内存前,都有一个mem_control_block结构:
struct mem_control_block
{
int is_available; //这块内存是否可用
int size; //这块内存的size;
};
我按照上面的说法,测试了一下:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<malloc.h>
int main()
{
int* start=sbrk(0);//在没有任何动态分配内存之前,取得堆的最低点,当然这时也是最高点
printf("the start of heap :%p\n",start);
int* ptr1=(int*)malloc(12);//申请一块12字节的内存
printf("the return val of malloc(12): %p\n",ptr1);
printf("and the end of heap now : %p\n",sbrk(0));//显示在动态分配内存后堆的最高点
for(int i=0;i<3;i++)
ptr1[i]=i+1;//让我们实际分配到的内存里面填充数据
int* ptr2=start;
for(int i=0;i<10;i++)
{
printf("%p:%x",ptr2,*ptr2);//看看堆里面都有些什么
ptr2++;
}
free(ptr1);//释放掉内存
ptr2=start;
for(int i=0;i<10;i++)
{
printf("%p:%x",ptr2,*ptr2);//看看堆里面还都有些什么
ptr2++;
}
return 0;
}
然后编译:
运行:
确实malloc返回后的指针前面有两个int大小的内存,按照帖子的说法,第一存的是is_available,就是截图中的0x8b74000,但是我们free后其值还是0,第二个存的是内存块的大小,这里用的是十六进制表示的,11就是17而这里加上该int的四字节,是16字节,所以size里面存放的不是size 而是size+1。具体有没有这个is_available字段呢?我看我接下来的第二段代码,改了一下(红色部分):
int main()
{
int* start=sbrk(0);//在没有任何动态分配内存之前,取得堆的最低点,当然这时也是最高点
printf("the start of heap :%p\n",start);
int* ptr1=(int*)malloc(12);//申请一块12字节的内存
int* ptr3=(int*)malloc(12);//附加的,我们再申请一块内存
int* ptr4=(int*)malloc(12);//附加的,我们申请第三块内存
printf("the return val of malloc(12): %p\n",ptr1);
printf("and the end of heap now : %p\n",sbrk(0));//显示在动态分配内存后堆的最高点
for(int i=0;i<3;i++)
ptr1[i]=i+1;//让我们实际分配到的内存里面填充数据
for(int i=0;i<3;i++)
ptr3[i]=i+1;//让我们实际分配到的内存里面填充数据
for(int i=0;i<3;i++)
ptr4[i]=i+1;//让我们实际分配到的内存里面填充数据
int* ptr2=start;
for(int i=0;i<20;i++)//将10改为20了
{
printf("%p:%x",ptr2,*ptr2);//看看堆里面都有些什么
ptr2++;
}
free(ptr1);//释放掉内存
free(ptr3);
free(ptr4);
ptr2=start;
for(int i=0;i<20;i++)//将10改为20了
{
printf("%p:%x",ptr2,*ptr2);//看看堆里面还都有些什么
ptr2++;
}
return 0;
}
然后编译运行 结果:
可以看到除了堆最开始的地方有那么一个4字节里面存的是0,接下来的每次malloc内存后面根本就没有有这个is-avaliable字段,没有这个4字节。
因此可以得出一个结论真实的malloc实现并不是那么回事,可能没有那个mem_control_block 结构,或者说至少,mem_control_block里面没有is_available 字段。还有一点,size里面存放的不是size 也不是size+4,更不是size+8,而是,size+4+1(假设sizeof(int)=4)。
虽然我在这儿只展示了两端代码,说明性不是很强,但是我其实做过很多个试验得出了在下面一些结论,可能表述有误,供大家参考,可以去验证(基于linux上的gcc,vc上的话,情况可能不同):
1.堆的最低点的4字节(一个int)永远是0,不是is_available.
2.当我上面的代码里面的malloc(12)换成malloc(16),时,你会惊奇的发现,每个malloc 出来的内存块的大小会是24,即size段存的是25,这样就比我们预计的16+4又多了4字节,而这个4字节,又刚好在size段的前面,难道是is_available?告诉你,不是,那是内存对齐空出来的,而且,内存对齐时空出来的大小也算在size字段里面,但是一般我们用sizeof时,返回值不会有内存对齐的考虑,这也是delete和delete[]不能混用的原因之一吧。
3.内存对齐可不止只有类或者结构体里面的内存对其,就是我们自己动手分配的内存(比如上面的ptr1,ptr3,ptr4)之间,也会有内存对其,不同平台可能不同,但linux下gcc编译出来的当分配的内存是用8来对齐的,考虑到size字段。但是当在结构体或者类里面考虑对齐时,由于没有size字段,是用4来对齐的,而在VC下面,是用最大的那个元素的大小来对齐的,就对其策略来说,gcc比vc更好些。
4.对于上面运行的结果,有几个值很特殊,我列下来
a)这里的20fd1是当前字节到堆最顶端的距离。该字节后面都是已经被系统映射,但是还没被进程分配的内存。
b)第一个 malloc出来的内存的第一个int位置在free后被改为0,但是接下来的后面几个字节却没有改
c)第二个 malloc出来的内存的第一个int位置在free后被改为0x8938000。
d)第三个malloc出来的内存的第一个int位置在free后被改为0x8938010.
为什么free后要改掉内存块的头一个4字节(即size后面的一个4字节)。而且,刚好被改为上一个内存块的起始地址。猜想是为了在内存管理时能够像前遍历。
至于那个is_available 到底在哪儿,那就要看真正的malloc的源码了。
就写这么多吧,第一次发帖,没经验,结构很乱,希望大家看了不要很恼火,同时希望大家指出我的错误。