堆上内存分配
1.brk()和sbrk()
progam break
program break记录了堆顶的地址,当使用brk或者sbrk系统调用时,program break的位置会随之改变
brk()
#include <unistd.h>
int brk(void *end_data_segment);
brk(void *end_data_segment)将program break 抬升到end_data_segment处,成功返回0,失败返回-1。
sbrk()
#include <unistd.h>
void *sbrk(intptr_t increment);
sbrk()将原本的program break再增加increment大小,成功返回前一个program break的地址,失败返回(void *)-1。此外,调用sbrk(0)将会返回当前program break的地址。
2.malloc和free
malloc()
#include <stdlib.h>
void *malloc(sizet_t size);
在堆上分配size字节大小的空间,成功则返回分配空间的起始地址,失败则返回NULL。
malloc分配内存失败的原因有可能是已经抵达系统所限制的program break上限
free()
#include <stdlib.h>
void free(void *ptr)
free()将malloc分配的空间回收,参数ptr为调用malloc返回的空间起始地址,这里可能会有一个疑问,我们只传递了一个地址给free,free如何知道要回收的空间大小呢。
实际上,malloc分配的内存块头部会记录这块内存块的大小。一般情况下,free并不会降低program break的位置,而是将free掉的内存块记录在空闲内存表中方便malloc再次使用,这么做的原因是:
- free释放掉的内存块并不总是在堆顶,program break并不会随着free而降低
- 减少sbrk()的次数,降低系统开销
- 对于大量持有内存程序经常反复malloc和free,降低program break的位置对程序来说并没有帮助
所以仅当堆顶有足够大量空闲区域时,free的glibc实现会将program break的位置降低,对"足够"的定义区别于molloc函数包中定义的参数(典型值为128KB)。
当使用malloc时,会优先从空闲内存列表中查找满足大小的空闲块,如果内存块刚好等于用户要求分配的大小,就把这块内存从空闲内存表中移除,并把地址返回给用户,如果大于用户要求分配的空间则会从空闲块中切割一部分出来分给用户。
在使用malloc和free时应遵守:
- 非经malloc函数包(包括malloc , calloc , realloc )分配的内存空间,禁止使用free释放。
- 在一个需要持续长时间运行的程序中,对于malloc分配的空间要及时释放。否则,堆将稳步增长直至达到虚拟内存空间的上限,再申请内存都将会失败,这个称为"内存泄漏"。
3.calloc和realloc
calloc
include <stdlib.h>
void *calloc(sizet_t num , sizet_t size);
分配一个num*size字节大小的内存空间,成功返回分配的内存块起始地址,失败返回NULL
他的通常用法是:
struct myStruct *p;
p = calloc(1000,sizeof(struct myStruct));
if(p == NULL)
errExit("calloc failed");
realloc
#include <stdlib.h>
void *realloc(void *ptr,sizet_t size);
realloc将malloc或者calloc分配的内存空间调整至size大小,并将原来的数据复制到当前地址块,成功返回分配的内存块起始地址,失败返回NULL。
提示:realloc返回的地址可能与传入的地址不同,当返回了不同的地址后,之前的地址不可再用
当调用realloc来增大空间时,realloc会试图合并ptr之后相邻的空闲块然后分配给用户,如果空闲块空间不足以满足要求时,realloc将会放弃ptr指向的内存块,寻找新的空闲块,并将原来的数据复制到找到的空闲内存块,然后返回新的地址。
2.栈上内存分配alloca
#include <alloca.h>
void *alloca(sizet_t size);
与malloc函数包不同alloca会在栈上分配空间,也不需要free来释放,不能通过realloc来修改大小,因为在函数中使用alloca分配的空间会随着函数结束而自动释放。
alloca分配速度要快于malloc,成功返回分配的内存起始地址,失败则返回NULL。