参考资料:代码随想录、阿秀
一、内存分区
(1)栈区
在执行函数时,函数内部局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但分配的内存有限。
(2)自由存储区
如果说堆是操作系统维护的⼀块内存,那么⾃由存储区就是C++中通过new和delete动态分配和释放 对象的抽象概念。需要注意的是,⾃由存储区和堆比较像,但不等价。
(3)堆区
用于存储动态分配的内存的区域,由程序员手动分配和释放。使用new和delete或者malloc和free进行堆内存的释放和分配。
(4)全局/静态存储区
全局变量和静态变量被分配到同⼀块内存中,在以前的C语⾔中,全局变量和静态变量又分为初始化的和未初始化的,在C++里面没有这个区分,它们共同占⽤同⼀块内存区,在该区定义的变量若没有初始化,则会被自动初始化。
(5)常量存储区
也被称为只读区,不允许修改,存储常量,如字符串常量等。
(6)代码区
存放函数体的二进制代码。
堆和栈的区别?
- 栈和堆都是用于存储程序数据的内存区域。
- 栈是⼀种有限的内存区域,用于存储局部变量、函数调用信息等。堆是⼀种动态分配的内存区域,用于存储程序运⾏时动态分配的数据。
- 栈上的变量生命周期与其所在函数的执行周期相同,而堆上的变量生命周期由程序员显式控制,分配(new 或 malloc)和释放( delete 或 free )。
- 栈上的内存分配和释放是自动的,速度较快。堆上的内存分配和释放需要手动操作,速度相对较慢。
二、内存泄漏(memory leak)
1. 什么是内存泄漏?
内存泄漏是指由于疏忽或错误造成了程序未能释放掉不再使用的内存的情况。内存泄漏并非指内存在物理上消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因而造成内存的浪费。
可以使用Valgrind、mtrace进行内存泄漏检测。
2. 内存泄漏有哪些类型?
(1)堆内存泄漏(Heap Leak)
堆内存指的是程序运行中根据需要,通过malloc,realloc new等从堆中分配的一块内存,完成后必须通过调用对应的 free或者 delete 删掉。如果由于程序的设计错误导致这部分内存没有被释放,那么此后这块内存将不会被使用,就会产生堆内存泄漏。
(2)系统资源泄漏(Resource Leak)
主要指程序使用系统分配的资源比如 Bitmap,handle,SOCKET 等没有使用相应的函数释放掉,导致系统资源的浪费,严重可导致系统效能降低,系统运行不稳定。
(3)没有将基类的析构函数定义为虚函数
当基类指针指向子类对象时,如果基类的析构函数不是 virtual,那么子类的析构函数将不会被调用,子类的资源没有正确释放,因此造成内存泄露。
3. 为什么会出现内存泄漏?
指针指向改变,未释放动态分配内存。
4. 如何防止内存泄漏?
(1)采用RAII机制(Resource Acquisition Is Initialization),将内存的分配封装在类中,构造函数分配内存,析构函数释放内存;C++RAII机制-CSDN博客
(2)使用智能指针【面试八股总结】C++11新特性:智能指针-CSDN博客
三、内存管理方式
1. malloc和free
- malloc:用于申请一块连续的指定大小的内存块区域,以void*类型返回分配的内存区域地址。
- calloc:与malloc相似,但是calloc() 会将所分配的内存空间中的每一位都初始化为零
- realloc: 给一个已经分配了地址的指针重新分配空间,可以做到对动态开辟内存大小的调整。
- free:用来释放动态开辟的内存
void* malloc(size_t size);- 如果开辟成功,则返回一个指向开辟好的空间的指针- 如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查- 返回值的类型是void*,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。
void free(void* ptr);- 如果参数ptr指向的空间不是动态开辟的,那free函数的行为是未定义的- 如果参数ptr是NULL指针,则函数声明式什么都不做
2. new和delete
new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。
- 注意:
- 1. operator new和operator delete不是对new和delete的重载,是两个库函数。
- 2. operator new实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间失败,尝试执行空间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。operator new本质是封装了malloc。operator delete本质是封装了free。
申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用new[ ]和delete[ ]
- 对内置类型
如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是: new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常,malloc会返回NULL。
- 对自定义类型
new 调用operator new函数申请空间,在申请的空间上执行构造函数,完成对象的构造;
3. 区别
特征 | new/delete | malloc/free |
---|---|---|
分配内存的位置 | 自由存储区 | 堆 |
内存分配失败 | 抛出异常 | 返回NULL |
分配内存的大小 | 编译器根据类型计算得出 | 显式指定字节数 |
处理数组 | 有处理数组的new版本new[] | 需要用户计算数组的大小后进行内存分配 |
已分配内存的扩张 | 不支持 | 使用realloc完成 |
分配内存时内存不足 | 可以指定处理函数或重新制定分配器 | 无法通过用户代码进行处理 |
是否可以重载 | 可以 | 不可以 |
构造函数与析构函数 | 调用 | 不调用 |