之前写过一篇博文,介绍当free()掉与malloc()返回指针不一致时导致崩溃原因:http://liulixiaoyao.blog.51cto.com/1361095/565677

这篇博文里面解释了崩溃是缘于指针变化之后,定位不到正确的内存管理头部导致;

今天想到一个问题,对于栈上的指针,指针的值是变量的地址,比如10元素的数组,可以直接通过p+i的方式访问,那对于堆上分配的内存,同样是malloc(10*sizeof(int)),由于前面或者某处需要一个管理头部对该内存块进行管理,malloc得到的指针指向的内存在哪儿?

如果指向管理头部,用p+i的方式明显会出错,则在堆和栈上两种指针操作会表现出不同结果,这如何可以?

要想两者表现一直,malloc返回的结果所指向的内存区域不包含管理头部的区域,但是可以通过固定的运算方式,找到管理头部的所在,这个操作由glibc在调用free()的时候进行

事实证明的确如此,free函数的源码大致如下:

  1. void free(void *ptr)    
  2.    {   
  3.            struct mem_control_block *free;   
  4.            free = ptr - sizeof(struct mem_control_block);   
  5.            free->is_available = 1;   
  6.            return;   
  7.    } 

管理头部的结构体定义如下:

  1. struct mem_control_block {   
  2.  int is_available;     
  3.  int size;             
  4.  }; 

如源码所示,可猜测malloc返回的指针指向实际可使用的内存地址,实际分配的内存块要大于请求的size(多出管理头部);free之时通过减去管理头部大小可获得实际分配的内存块,可解决上面的疑虑

 

接下来又引出另外一个问题,在free函数中,我们并没有看见实际的内存释放函数的调用(当然这时候释放必然不是调用free,free只是由glibc实现的包装),只有 free->is_available = 1;   这句有点实际意义,那内存在什么时机被释放呢?

有网友说是,is_avaliable是个标记,指示该块内存可被释放,已没有人使用,后续的释放操作由操作系统定时扫描,释放掉不再使用的内存块

is_avaliable是标记之用,的确没错,是显而易见的事情,至于实际的内存释放由操作系统定时扫描释放,却并不见得如此;

记得曾经了解到,glibc的malloc的实现,也是有预分配策略的,请求10字节,可能会分配100字节甚至更多(具体策略未详),这里的分配释放应该都是由glibc库管理的,应该清楚,glibc是用户空间代码,用户空间代码分配的内存是不会由操作系统释放的,操作系统定期会扫描释放内存不错,不过那应该是针对内核级别的,比如slab,文件系统page页面缓存等使用了预分配策略的,用户空间已使用系统调用brk等进行分配的内存空间对内核来说是实际被使用中的,不可能被内核回收

 

另,记录一下,malloc众所周知是在堆上分配内存,堆的空间是可以申请调大的;貌似记得malloc分配的空间也可能不是来自于堆,有可能是mmap的区域,描述可能不准,有待查证,仅作记录