1.C++五种存储区域:
1.1 堆
- 定义:在内存管理的语境下,指的是动态分配内存的区域。这个堆跟数据结构里的堆不是一回事。这里的内存,被分配之后需要手工释放,否则,就会造成内存泄漏。
- C++ 标准里一个相关概念是自由存储区,英文是 free store,特指使用 new和 delete来分配和释放内存的区域。一般而言,这是堆的一个子集。new和 delete操作的区域是 free store,malloc和free操作的区域是 heap,但 new和 delete 通常底层使用malloc和 free来实现,所以free store也是heap。
- 堆通常进行的操作:让内存管理器分配一个某个大小的内存块,让内存管理器释放一个之前分配的内存块。
- 生长方向:对于堆来说,生长方向是向上的,也就是说向着内存地址增加的方向。
1.2 栈
- 定义:在内存管理的语境下,指的是函数调用过程中产生的局部变量和函数形参的区域。这个栈和数据结构里的栈高度相似,都满足“后进先出”的规则。
- 栈通常进行的操作:
1)栈上的分配极为简单,移动一下栈指针而已。
2)栈上的释放也极为简单,函数执行结束时移动一下栈指针即可。
3)由于后进先出的执行过程,不可能出现内存碎片。
- 执行效率:栈区的执行效率高于堆区。
- 生长方向:对于栈来说,生长方向是向下的,也就是说向着内存地址减小的方向。
- 函数调用参数入栈顺序:参数的入栈顺序一般是从右向左。
- 指针存放的位置:指针不管指向任何类型,指针自身的位置只可能在栈区和全局静态区,函数内定义(包括形参)在栈区,函数外定义在全局静态区。
1.3 全局静态区
- 定义:全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区,程序结束释放。
1.4 代码区
- 定义:存放函数体等二进制代码;
1.5 常量区
- 定义:这是一块比较特殊的存储区,他们里面存放的是const,不允许修改。
2.malloc、calloc、realloc底层实现原理
2.1 malloc底层实现原理
2.1.1 malloc分配简单结论
- 当开辟的空间小于128K时,调用sbrk函数,malloc的底层实现是系统调用函数brk,其主要移动指针
_enddata
(此时的 _enddata指的是Linux地址空间中堆段的末尾地址,不是数据段的末尾地址)。 - 当开辟的空间大于128K时,mmap系统调用函数来在文件映射区域找一块空间来开辟。
- 这两种方式分配的都是虚拟内存,没有分配物理内存。在第一次访问已分配的虚拟地址空间的时候,发生缺页中断,操作系统负责分配物理内存,然后建立虚拟内存和物理内存之间的映射关系。
2.1.2 malloc分配详细过程
2.1.2.1 malloc小于128K的内存,使用brk分配
- 将_enddata往高地址推(只分配虚拟空间,不对应物理内存,因为没有初始化),第一次读/写数据时,引起内核缺页中断,内核才分配对应的物理内存,然后虚拟地址空间建立映射关系。
- 当malloc(30K)的时候,malloc函数会调用brk系统调用,将_enddata指针往高地址推30K,就完成虚拟内存分配。事实是:_endata+30K只是完成虚拟地址的分配,这块内存现在还是没有物理页与之对应的,等到进程第一次读写A这块内存的时候,发生缺页中断,这个时候,内核才分配A这块内存对应的物理页。也就是说,如果用malloc分配这块内容,然后从来不访问它,那么对应的物理页是不会被分配的。
- 如下图所示:当进程调用free(B),B对应的虚拟内存和物理内存都没有释放,因为只有一个_endata指针,如果往回推,那么D这块内存怎么办呢?当然,B这块内存,是可以重用,如果这个时候再来一个40K的请求,那么malloc很可能就把B这块内存返回回去。