内存分配
2015-11-20 17:19
253 查看
我们已经知道,每一个进程内存包含多个段,如文本段、已初始化数据段、未初始化数据段和堆栈段等。程序中已初始化的全局变量和静态变量会在已初始化数据段中,而未初始化的数据在未初始化数据段中。对于这些数据,程序运行的时候才分配内存,这里的分配内存,是由系统程序完成的。
系统程序需要为动态数据结构(例如链表和二叉树等)分配额外的内存,这类的数据结构的大小由运行时所获取的信息决定。内存分配可以在堆或堆栈上进行。
下图是进程内存布局图:
1、在堆上分配内存
图中,堆是一段长度可变的连续虚拟内存,开始于未初始化数据段的末尾,随着内存的分配和释放而增减。可以通过改变堆的大小来实现内存的分配。通常将堆的当前内存边界称为“program break”。
C语言中可以使用malloc()实现内存分配,malloc是基于brk()和sbrk()系统调用实现的。brk和sbrk可以调整program break,由于program break就是未使用内存和堆之间的界限,因此修改program break就是改变堆的大小。最开始的时候,program break在未初始化数据段的末尾,和&end位置相同。
需要注意的是,分配的内存是虚拟内存,并没有分配实际物理内存,内核会在进程首次访问这些虚拟内存时自动分配新的物理内存分页。下面是brk和sbrk系统调用的具体格式:
brk将program break设置为参数end_data_segment,注意,虚拟内存以页为单位进行分配,end_data_segment会和下一个内存页的边界处对齐。同时还要注意,当试图将program break设置为比当前值小的位置时,可能会发生意想不到的错误。
sbrk系统调用将program break增加参数increment大小。intptr_t是正数数据类型。如果调用成功,sbrk返回前一个program break的地址。也就是说,如果增加program break,那么sbrk将返回新分配的内存的起始位置的指针。
小技巧:可以调用sbrk(0)得到当前program break的位置。
C语言中的内存分配函数malloc和内存释放函数free是基于brk和sbrk系统调用实现的。下面是具体格式:
由于以下的原因,free函数通常不降低program break的位置:
被释放的内存块通常会位于堆的中间,而非堆的顶部;
这样减少了sbrk系统调用次数;
在大多数情况下,降低program break的位置不会对那些分配大量内存的程序有多少帮助,因为它们通常倾向于持有已分配内存或是反复释放和重新分配内存,而非释放所有内存后再持续运行一段时间。
下面的程序说明了free对program break的影响。程序有多个参数,第一个参数是分配内存的块数,第二个参数是每个内存块的大小,第三个参数指定了释放内存块的步长,第四个和第五个参数限定了释放内存块的范围,如果不指定,就当全部范围。
下面给出几个运行示例:
(1)分配1000个内存块,每个10240字节,步长是1,全部释放
可以看到,当释放的内存足够大的时候,free会降低program break;
(2)连续释放前999个内存块
结果program break没有变化,因为释放的内存块在堆的中间;
(3)每隔一个内存块释放一个
program break也没有变化
(4)释放后500块内存块
program break下降了,free使用sbrk系统调用实现。
free函数会在释放是将相邻的内存块合并为一个连续的内存块。
2、在堆栈上分配内存
alloca函数通过增加栈帧的大小来分配内存。格式如下:
系统程序需要为动态数据结构(例如链表和二叉树等)分配额外的内存,这类的数据结构的大小由运行时所获取的信息决定。内存分配可以在堆或堆栈上进行。
下图是进程内存布局图:
1、在堆上分配内存
图中,堆是一段长度可变的连续虚拟内存,开始于未初始化数据段的末尾,随着内存的分配和释放而增减。可以通过改变堆的大小来实现内存的分配。通常将堆的当前内存边界称为“program break”。
C语言中可以使用malloc()实现内存分配,malloc是基于brk()和sbrk()系统调用实现的。brk和sbrk可以调整program break,由于program break就是未使用内存和堆之间的界限,因此修改program break就是改变堆的大小。最开始的时候,program break在未初始化数据段的末尾,和&end位置相同。
需要注意的是,分配的内存是虚拟内存,并没有分配实际物理内存,内核会在进程首次访问这些虚拟内存时自动分配新的物理内存分页。下面是brk和sbrk系统调用的具体格式:
#include <unistd.h> int brk(void *end_data_segment);//Returns 0 on success,or -1 on error void *sbrk(intptr_t increment);//Returns previous program break on success,or (void *)-1 on error
brk将program break设置为参数end_data_segment,注意,虚拟内存以页为单位进行分配,end_data_segment会和下一个内存页的边界处对齐。同时还要注意,当试图将program break设置为比当前值小的位置时,可能会发生意想不到的错误。
sbrk系统调用将program break增加参数increment大小。intptr_t是正数数据类型。如果调用成功,sbrk返回前一个program break的地址。也就是说,如果增加program break,那么sbrk将返回新分配的内存的起始位置的指针。
小技巧:可以调用sbrk(0)得到当前program break的位置。
C语言中的内存分配函数malloc和内存释放函数free是基于brk和sbrk系统调用实现的。下面是具体格式:
#include <stdlib.h> void *malloc(size_t size);//Returns pointer to allocated memory on success,or NULL on error void free(void *ptr);malloc函数返回新分配内存的起始位置的指针,如果无法分配内存,则返回NULL;free函数释放参数ptr指向的内存块。
由于以下的原因,free函数通常不降低program break的位置:
被释放的内存块通常会位于堆的中间,而非堆的顶部;
这样减少了sbrk系统调用次数;
在大多数情况下,降低program break的位置不会对那些分配大量内存的程序有多少帮助,因为它们通常倾向于持有已分配内存或是反复释放和重新分配内存,而非释放所有内存后再持续运行一段时间。
下面的程序说明了free对program break的影响。程序有多个参数,第一个参数是分配内存的块数,第二个参数是每个内存块的大小,第三个参数指定了释放内存块的步长,第四个和第五个参数限定了释放内存块的范围,如果不指定,就当全部范围。
#include <unistd.h> #include <stdlib.h> #include "error_functions.c" #include "get_num.c" #define MAX_ALLOCS 1000000 int main(int argc,char *argv[]) { if(argc<3||strcmp(argv[1],"--help")==0) usageErr("%s num_blocks block_size [step [min [max]]]\n",argv[0]); char *ptr[MAX_ALLOCS]; int freeStep,freeMin,freeMax,blockSize,blockNum; blockNum=getInt(argv[1],GN_GT_0,"block_num"); if(blockNum>MAX_ALLOCS) errExit("block_num>MAX_ALLOCS\n"); blockSize=getInt(argv[2],GN_GT_0|GN_ANY_BASE,"block_size"); freeStep=(argc>3)?getInt(argv[3],GN_GT_0,"step"):1; freeMin=(argc>4)?getInt(argv[4],GN_GT_0,"min"):1; freeMax=(argc>5)?getInt(argv[5],GN_GT_0,"max"):blockNum; if(freeMax>blockNum) errExit("bigger"); printf("Initial program break: %10p\n",sbrk(0)); int j=0; for(j=0;j<blockNum;j++) { if((ptr[j]=malloc(blockSize))==NULL) errExit("malloc"); } printf("Program break is now: %10p\n",sbrk(0)); for(j=freeMin-1;j<freeMax;j+=freeStep) free(ptr[j]); printf("After free(),program break is:%10p\n",sbrk(0)); exit(EXIT_SUCCESS); }
下面给出几个运行示例:
(1)分配1000个内存块,每个10240字节,步长是1,全部释放
可以看到,当释放的内存足够大的时候,free会降低program break;
(2)连续释放前999个内存块
结果program break没有变化,因为释放的内存块在堆的中间;
(3)每隔一个内存块释放一个
program break也没有变化
(4)释放后500块内存块
program break下降了,free使用sbrk系统调用实现。
free函数会在释放是将相邻的内存块合并为一个连续的内存块。
2、在堆栈上分配内存
alloca函数通过增加栈帧的大小来分配内存。格式如下:
#include <alloca.h> void *alloca(size_t size);//Returns pointer to allocated block of memoryalloca函数有几个优点,一个就是速度快,因为编译器将它作为内联代码处理。还有,alloca分配的内存会随栈帧的移除而自动释放,即当调用alloca的函数返回时。这是因为函数返回时所执行的代码会重置栈指针寄存器,使其指向前一帧的末尾。
相关文章推荐
- AT+CSMP 设置短消息文本参数
- 详解linux互斥锁 pthread_mutex和条件变量pthread_cond
- "Java 反序列化"过程远程命令执行漏洞
- AT+CSMP 设置短消息文本参数
- Fragment
- linux学习笔记<命令介绍>
- 小学奥数(下)
- Spring 中的事件监听的实现
- 产生唯一文件
- 旭说数据结构之二叉查找树
- Codeforces Beta Round #85 (Div. 1 Only) B. Petya and Divisors 暴力
- C++中头文件、源文件之间的区别与联系
- roscore报错
- ROWNUMBER() OVER( PARTITION BY COL1 ORDER BY COL2)用法
- label不同颜色不同字体及加删除线
- jQuery-1.9.1源码分析系列(十)事件系统之事件包装
- I love ACM
- IPv6 NDP邻居发现协议 1
- 分割
- ssh和ssh2之间的免密码登陆详解