内存中堆和栈的大小在加载的时候是固定的
也就是说,可执行文件加载到内存中的时候,就给栈和堆划分了固定大小的空间,vm_area_struct结构体指明了一个连续区域的头地址和尾地址。
所谓的栈和堆的动态增长,并不是指堆和栈空间的头尾指针发生变化,它们是不会改变的。变的只是两个指针:栈顶的指针和堆顶的指针。
malloc的两种方法
malloc当然会先从空闲页面中找合适大小的页面。但是如果找不到,需要分配新的页面。这时候有两种方法:
- brk()调用,将堆顶的指针brk往上推
- mmap()调用,在文件映射区域(栈和堆之间),分配一片新的空间,这片新的空间有单独的vm_area_struct结构
如果要分配的内存比较小会用第一种方法,如果比较大会用第二种方法
brk()的优点和缺点
brk()要做的仅仅是修改brk指针,在用户空间实现,不需要陷入内核,比较简单省时间;而mmap()要在内核分配新的vm_area_struct结构体,必须陷入内核。
堆中内存的free
free是由运行库实现,它只是在已分配的堆块前面加一个可用标志,并不实际释放内存,不论是物理内存还是进程的堆空间。
在下次的malloc时,这块空间可能被重用。
如果进程的堆空间出现较多的碎片(这是逻辑地址中的碎片),运行库的堆管理例程会移动/合并碎片,此时可能会出现物理内存的释放/重新分配。
而对于brk指针,只有它指向的那片内存被free的时候才会下移。比如先malloc了一个A,然后malloc了一个B。free掉A之后,brk是不会下移的;free掉B的时候brk才会下移。