之前写过一篇博文,介绍当free()掉与malloc()返回指针不一致时导致崩溃原因:http://liulixiaoyao.blog.51cto.com/1361095/565677
这篇博文里面解释了崩溃是缘于指针变化之后,定位不到正确的内存管理头部导致;
今天想到一个问题,对于栈上的指针,指针的值是变量的地址,比如10元素的数组,可以直接通过p+i的方式访问,那对于堆上分配的内存,同样是malloc(10*sizeof(int)),由于前面或者某处需要一个管理头部对该内存块进行管理,malloc得到的指针指向的内存在哪儿?
如果指向管理头部,用p+i的方式明显会出错,则在堆和栈上两种指针操作会表现出不同结果,这如何可以?
要想两者表现一直,malloc返回的结果所指向的内存区域不包含管理头部的区域,但是可以通过固定的运算方式,找到管理头部的所在,这个操作由glibc在调用free()的时候进行
事实证明的确如此,free函数的源码大致如下:
- void free(void *ptr)
- {
- struct mem_control_block *free;
- free = ptr - sizeof(struct mem_control_block);
- free->is_available = 1;
- return;
- }
管理头部的结构体定义如下:
- struct mem_control_block {
- int is_available;
- int size;
- };
如源码所示,可猜测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的区域,描述可能不准,有待查证,仅作记录