动态内存分配

堆是一段长度可变的连续虚拟内存。始于BBS段的末尾,随着内存的分配和释放而增减,堆的当前内存边界成为 program break

linux提供的系统调用:brk()和sbrk()。

#include<uinstd.h>

//return 0 on success,or -1 on error
int brk(void*end_data_segment);

//return previous program break on success,or(void*)-1 on error
void*sbrk(intptr_t increment);

brk将program break设置为参数end_data_segment所指定的位置,由于虚拟内存分配以页为单位,end_data_segment回到下一个内存页的边界处。

sbrk()在原地址上增加参数increment的大小。使用sbrk(0)可以获得program break当前位置。

  增量为正数时,分配内存
  增量为负数时,回收内存

program break的位置抬升以后,程序可以访问新分配区域内的任何内存地址,而此时物理内存页尚未分配,内核会在进程首次试图访问这些虚拟内存地址时自动分配新的物理内存页。

 

 


malloc函数

#include<stdlib.h>
void*malloc(size_t size);

void free(void*ptr);

malloc有以下有点

1.属于C语言的一部分

2.更易于在多线程程序中使用

3.接口简单,允许分配小块内存

4.允许随意释放内存块,他们被维护于一张空闲内存列表中,在后续内存分配调用时循环使用。

 

malloc返回的类型void*,可以赋值给任意类型的C指针。malloc返回内存块是基于8字节或者16字节边界来分配内存的。

一般情况下,free()并不会降低program break的位置,而是将这块内存添加到空闲内存列表中,供后续的malloc函数循环使用。

这么做的原因如下:

1.被释放的内存块通常位于堆的中间,而非堆的顶部,因此降低program break不可能。

2.减少了程序必须执行的sbrk的调用次数。

3.在大多数情况下,降低program break 的位置不会堆那些分配大量内存的程序有多少帮助,因为它们通常倾向于持有已分配内存或是反复释放和重新分配内存,而非释放所有内存后再持续运行一段时间。 


malloc和free的实现

malloc()会首先扫描之前由free()所释放的空先内存块列表,以找到尺寸大于或等于要求的一块内存,如果这一块内存的尺寸正好与要求相当,就直接返回给调用者,如果是一块较大的内存,那么将对其进行分割,把大小相当的一块返回给调用者,把剩余的那块空先内存块保留在空闲列表中。如果空闲列表中没有足够大的空闲内存块,那么malloc()会调用sbrk()以分配更多的内存,为了减少对sbrk()的调用,malloc()没有严格按照要求的字节数分配内存,而是以虚拟内存页大小的倍数增加sbrk(),超出的部分置于空闲内存列表中。

 

 

malloc()在分配内存块时,会额外分配几个字节来记录这块内存大小的整数值。该整数就位于内存块的起始处,而实际返回给调用者的内存地址恰好位于这一长度记录字节之后。

所以free()才能知道释放多少大小。当将内存至于空闲内存列表时,free()会使用内存本身的空间来存放链表指针,将自身加入到列表中。

 

 要确保释放已经使用完毕的内存,否则堆将一直增长,直到抵达可用虚拟内存的上限,在此之后分配内存的任何尝试都将失败,这种情况被称作内存泄漏。


 

#include<stdlib.h>
void*calloc(size_t numitems,size_t size);

  calloc()用于给以组相同对象分配内存,calloc()会将已分配的内存初始化为0。


 

realloc(void*ptr,size_t size)

size是希望调整后的总大小。


 

posted on 2019-09-05 16:55  长岛的雪  阅读(301)  评论(0编辑  收藏  举报