动态申请内存

int main() {
    int *p = (int *)calloc(sizeof(int), 1);
    int *t = (int *)calloc(sizeof(int), 1);
    cout << "栈,向低地址(向下)生长:";
    print(&p, &t);
    cout << "堆,向高地址(向上)生长:";
    print(p, t);

    free(p);    //堆中需要自己释放
    free(t);

    p = (int *)alloca(sizeof(int));
    t = (int *)alloca(sizeof(int));
    cout << "alloca(size) 在栈内申请内存:";
    print(p, t);    //地址向下生长;

//    free(p);    //栈中会随着函数的退出自动释放
//    free(t);

    return 0;
}

输出:

栈,向低地址(向下)生长:0x7ffd17f7cd58 0x7ffd17f7cd50
堆,向高地址(向上)生长:0x5581efc03c20 0x5581efc03c40
alloca(size) 在栈内申请内存:0x7ffd17f7cd30 0x7ffd17f7cd10

引用几张图:

内存分布





程序内存空间(代码段、数据段、堆栈段)

栈、堆辨析:

1、栈区(stack):由编译器自动分配释放 ,存放函数的参数值、局部变量的值等。其操作方式类似于数据结构中的栈。 

2、堆区(heap):由程序员分配释放, 若程序员不释放,程序结束时可能由操作系统回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。



brk()和sbrk()函数的使用

堆的管理

  上面的函数我们其实很少使用,大部分我们使用的是mallocfree函数来分配和释放内存。这样能够提高程序的性能,不是每次分配内存都调用brksbrk,而是重用前面空闲的内存空间。brksbrk分配的堆空间类似于缓冲池,每次malloc从缓冲池获得内存,如果缓冲池不够了,再调用brksbrk扩充缓冲池,直到达到缓冲池大小的上限,free则将应用程序使用的内存空间归还给缓冲池。

  如果缓冲池需要扩充时,一次扩充多少呢?先运行下面的程序看看:

复制代码
 1 #include <stdio.h>
 2 #include <unistd.h>
 3 #include <stdlib.h>
 4  
 5 int main(void)
 6 {
 7         void *tret;
 8         char *pmem;
 9         
10         tret = sbrk(0);
11         if (tret != (void *)-1)
12                 printf ("heap start: %p\n", tret);
13                 
14         pmem = (char *)malloc(64);  //分配内存
15         if (pmem == NULL) {
16                 perror("malloc");
17                 exit (EXIT_FAILURE);
18         }
19         printf ("pmem:%p\n", pmem);
20         tret = sbrk(0);
21         if (tret != (void *)-1)
22                 printf ("heap size on each load: %p\n", (char *)tret - pmem);
23     free(pmem)
24     return 0;
25 }
复制代码

  运行结果如下:

  从结果可以看出调用malloc(64)后缓冲池大小从0变成了0x20ff8,将上面的malloc(64)改成malloc(1)结果也是一样,只要malloc分配的内存数量不超过0x20ff8,缓冲池都是默认扩充0x20ff8大小。值得注意的是如果malloc一次分配的内存超过了0x20ff8,malloc不再从堆中分配空间,而是使用mmap()这个系统调用从映射区寻找可用的内存空间


接下来就是要对用brk和mmap分配好内存进行管理了。因为brk(),mmap()是系统调用,如果每次调用malloc动态分配内存都执行一次系统调用,那开销是比较大的。再者,如果每次申请的内存较小,但是系统分配的内存都是固定大小的倍数(一般是4KB,一页),这样就会有大量的浪费。所以malloc一般会实现一个内存堆来管理这些内存,malloc分配的内存都会以若干chunk的方式放到内存堆中。每次用户调用malloc动态分配内存的时候,malloc会先到内存堆里进行查找,如果内存堆里没有合适的空闲chunk,再利用brk/malloc系统调用分配一大块内存,然后把新分配的大块内存放到内存堆中,并生成一块合适的chunk块返回给用户。当用户用free释放chunk的时候,可能并不立即使用系统调用释放内存,而是将释放的chunk作为空闲chunk加入内存堆中,和其他的空闲chunk合并,便于下次分配的时候再次使用。
    
一般说来,释放的chunk如果标记为mmap申请的,则使用munmap释放。如果是brk申请的,进一步判断堆顶之下的空闲chunk是否大于128KB,如果是,则使用brk()释放。如果小于128KB,仍由内存堆维护。这样对brk()的使用就会有个问题,当brk()释放的内存块在堆顶之下,且内存块到堆顶之间还有未释放的内存。那么这块内存的释放将不会成功,从而形成内存空洞。

1. 无论是堆,还是栈都是对虚存的操作和管理。 
2. 系统调用brk()和mmap()用来动态分配虚存空间,也就是表明这些虚存地址是合法的,访问的时候,系统应为其分配物理内存,而不是报错。 
3. 堆的本质是动态申请的虚存空间。理论上可以用任何方式去管理这块空间。但数据结构--"堆"是最常用的一种,所以这块分配的空间常称为被堆。 
4. 和栈不一样,堆的管理是在用户函数库中进行,malloc/free等函数是堆的入口。 
5. 每次分配的内存块大小都会被记录下来,释放的时候只需要指定要释放的内存地址就行了。这就是为什么malloc的时候要指定大小,free的时候不用。 
6. 堆和栈一样,仍然使用了物理内存的延迟分配策略。



猜你喜欢

转载自blog.csdn.net/M_N_N/article/details/80435357