文章目录
1、在堆上分配内存
进程通过增加堆的大小来分配内存,堆是一段长度可变的连续虚拟内存,始于进程的未初始化数据段末尾;
【program break】:堆当前的边界,超过会引发未知的错误;
- 可设置的精确上限取决于一系列因素(内存映射、共享内存段,共享库的位置);
2、调整program break: brk和sbrk
program break位置抬升后,程序可以访问新分配区域内的任何内存地址,但物理内存页尚未分配;
- 内核会在进程首次访问虚拟地址空间时自动分配新的物理内存页;
#include <unistd.h>
int brk(void *addr);
/**
@func: 会将program break设置为参数end_data_segment所指定的位置,由于虚拟内存以页为单位分配,end_data_segment会被
四舍五入到下一个内存页的边界处;
*/
void *sbrk(intptr_t increment);
/**
@func:将program break在原有地址上增加从参数increment传入的大小;
return: 返回指向这块新分配内存起始位置的指针;
*/
2.1 malloc和free
#include <stdlib.h>
void *malloc(size_t size);
/**
@func: 允许分配小区块,且随意释放内存;
所分配的内存为初始化;
return: 返回指向新分配内存起始位置处指针;
*/
void free(void* ptr);
/**
@func: 释放内存,不会降低program break的位置,而是将这块内存添加到空闲内存列表,供后续循环使用;
【为何要循环使用?】
- 被释放的内存块通常会位于堆的中间,而非堆的顶部,因而降低 porgram break是不可能的;
- 它最大限度地减少了程序必须执行的sbrk()调用次数;
- 在大多数情况下,降低program break的位置不会对那些分配大量内存的程序有多少帮助;
*/
在glibc中free会调用sbrk来降低program break地址;
2.2 malloc和free的实现
- malloc使用时会线扫描free所释放的空闲内存块列表,来找到一块大于会等于的空闲内存块;
- 为了减少堆sbrk的调用,mallc按大幅度以虚拟内存页大小的倍数,来增加program break,超出部分将被闲置;
控制和检测malloc函数包
- mallopt通过修改参数来控制malloc采用的算法;
- mallinfo返回一个结构,分配内存的各种统计数据;
2.3 堆分配内存的其他方法
#include <stdlib.h>
void *calloc(size_t numiterms, size_t size);
/**
@func: 给一组相同对象分配内存,并初始化为0;
@param numiterms: 分配对象的数量;
@param size: 每个对象的大小;
*/
void *realloc(void *ptr, size_t size);
/**
@func: 一般用来增加一块内存的大小,此前的区块由malloc所分配,不会对额外分配的区块进行初始化;
@param ptr:需要调整的指针区块;
@param size: 所需调整的大小;
return: 返回调整大小的内存块的指针;
*/
【注意】:应尽量少使用,会占用大量的cpu;
2.4 在堆栈上分配内存alloca
增加栈帧的大小,从堆栈上分配,当前调用函数的栈帧位于堆栈的顶部,帧的上方存在扩展空间,只需修改堆栈指针值即可;
#include <alloca.h>
void *alloca(size_t size);
/**
@func: 再堆栈上分配内存,该内存块不需要维护,会随着栈帧的移除而自动释放;
@param size: 指定在堆栈上分配的字节数;
return: 指向已分配内存块的指针;
*/
【注意】:不能再一个函数的参数列表中调用alloca;