C++内存概述
在C++中,内存被分为五个区域:堆、栈、自由存储区、静态/全局存储区和常量存储区。
- 静态存储区:静态内存用来保存局部static对象、类static数据成员、以及定义在任何函数之外的变量(全局变量)。
- 栈内存:栈内存用来保存定义在函数内的非static对象。
- 堆内存:使用new关键字开辟的内存空间就位于堆内存,有程序员释放(不由编译器负责),一般一个new对应一个delete,一个new[]对应一个delete[],如果程序员没有释放,在程序结束时,系统会自动收回资源。
- 自由存储区:是*alloc分配的内存空间即位于自由空间,使用free来释放
- 常量存储区:一块特殊的存储区,里面存放常量,不允许修改。
堆和自由空间其实是一个整体的内存池,属于同一块区域,new的底层实现调用了malloc,new可以看做是malloc智能化的高级版本。
堆VS栈
堆内存和栈内存是内存中两块不同的区域,他们在诸多方面既有区别又有联系:
- 管理方式:
- 堆中的资源有程序员来控制,有时我们忘记释放内存,会造成内存泄漏(memory leak);有时某内存单元中的数据尚有指针在引用,我们就是放了该内存,就会产生引用非法内存的指针。
- 栈资源由编译器自动管理,无需手动控制。
- 系统响应:
- 堆,对于栈,系统中有一个记录空闲内存地址的指针,当系统收到程序申请时,遍历该链表,寻找第一个空间大于申请空间的堆节点,将该节点从空闲链表中删除,并将其分配给程序。如果该堆内存块的大小恰好等于申请空间大小,则直接将首地址返回给用户;如果内存块的大小大于所申请大小,则将此块分裂,将剩余部分的堆内存留在可用堆内存中,以“堆链表”的形式和其他未被分配的内存关联。如果整个堆链表内都未找到满足大小的堆内存块,系统将会给“堆链表”链接一个更大的区域供其使用,若这一步骤依然失败,malloc则将返回NULL,并向程序员报错。
- 栈,只要栈的剩余空间大于所申请空间,系统为程序提供内存,否则报出异常提示栈溢出。
- 空间大小
- 堆:堆不是连续的内存区域,因为系统使用链表来存储空闲空间,堆大小受限于计算机系统中有效的虚拟内存(32位系统理论上是4G),所以堆的空间比较灵活,比较大。
- 栈:栈是一块连续的内存区域,大小是操作系统预定好的,是一个编译时就确定的常数。如果申请空间超过栈的剩余空间,将提示overflow。因此能从栈获得的空间较小。
- 碎片问题
- 堆:对于堆,频繁的使用new/delete会造成大量碎片,是程序效率降低。
- 栈:对于栈,是一个先进后出的队列,进出一一对应,不会产生碎片。
- 生长方向
- 堆:堆向上,由低地址向高地址扩展。
- 栈:栈向下,由高地址向低地址扩展。
- 分配效率
- 堆:堆是由new分配的内存,都是动态分配,一般速度较慢,而且容易产生碎片,不用使用方便。
- 栈:栈由系统自动分配,既有动态分配也有静态分配。速度较快,但程序员无法控制。
malloc和free
malloc
根据标准C库函数的定义,malloc具有如下如下原型:
void* malloc(size_t size);
这个函数要实现的功能是在系统中分配一段连续的可用的内存,具体要求如下:
- malloc分配的内存大小至少为size参数所指定的字节数。
- malloc的返回值是一个指针,指向一段可用内存的起始地址。
- 多次调用malloc所分配的地址不能有重叠部分,除非某次malloc所分配的地址被释放。
- malloc应尽快完成内存分配并返回(不使用NP-hard的内存分配算法)。
- 实现malloc时应实现内存大小调整和内存释放函数(realloc,free)
free
free函数用来释放之前调用malloc、calloc、realloc所分配的内存空间,具有如下原型:
void free(void *ptr);
free的实现并不是看上去那么简单,要进行free,首先要解决两个关键问题:
- 1.如何验证传入的地址是有效的,即确定是通过malloc方式分配的数据区首地址。要解决这一问题,首先要确定改地址在之前malloc所分配的区域内,即在first_block和当前break指针范围内;其次这个地址确实是之前通过malloc分配的。
- 2.如何解决碎片问题。
new和delete
在C++中,动态内存的管理师通过一对运算符来完成的。new:在动态内存中为对象分配空间并返回一个指向该对象的指针,我们可以选择对对象进行初始化。delete:接受一个动态对象的指针,销毁该对象,并释放与之关联的内存。
malloc和new的区别
- malloc/free是标准库函数,new/delete是C++运算符;
- malloc失败返回空,new失败抛出异常;
- new/delete会调用构造、析构函数,malloc/free不会,所以他么无法满足动态对象的要求。
- new返回有类型的指针,malloc返回无类型的指针。
智能指针
为了更容易也更安全的使用动态内存,C++新的标准库提供了智能指针类型来管理动态对象。它与正常指针类似,重要的区别是它负责自动释放所指的对象。因为智能指针也是一个类,当超出类的作用域时,类会自动调用析构函数,析构函数会自动释放资源。所以智能指针的作用原理就是在函数结束时自动释放内存空间,而不需要手动释放内存空间。
- shared_ptr允许多个指针指向同一个对象,该对象何其相关的资源会在“最后一个引用被撤销”的时候释放。它使用计数机制来表明资源被几个指针共享,可用通过成员函数use_count()来查看资源的所有者个数。
- unique_ptr实现独占拥有或者严格拥有概念,保证同一时间内只有一个智能指针可以指向该对象。对于避免资源泄露特别有用
- weak_ptr是一种不控制生命周期的智能指针,它指向一个shared_ptr管理的对象。进行该对象的内存管理的是强引用的shared_ptr,weak_ptr只是提供了对管理对象的一个访问手段。weak_ptr设计的目的是为了配合shared_ptr而引入的之中智能指针来协助shared_ptr工作,它只可以从一个shared_ptr或另一个weak_ptr对象构造,他的构造和析构函数不会引起计数的增加或减少。weak_ptr是用来解决shared_ptr相互引用时的思索问题,如果两个shared_ptr相互引用,那么这两个指针的引用计数永远不能下降为0,资源永远不会释放。