一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第12天,点击查看活动详情。
一、面试题
💦 malloc/free和new/delete的区别
malloc/free 和 new/delete 的共同点是:都是从堆上申请空间,并且需要用户手动释放。不同的地方是:
这里主要从特点和用法、底层原理 (本质区别)、处理错误的方式三个方面进行说明
- malloc 和 free是函数,new 和 delete 是操作符
- malloc 申请空间时,需要手动计算空间大小并传递,new 只需在其后跟上空间的类型即可
- malloc 的返回值为 void*, 在使用时必须强转,new 不需要,因为 new 后跟的是空间的类型
- malloc 申请空间失败时,返回的是 NULL,因此使用时必须判空,new 不需要,但是 new 需要捕获异常
- 申请自定义类型对象时,malloc/free 只会开辟空间,不会调用构造函数与析构函数,而 new 在申请空间后会调用构造函数完成对象的初始化,delete 在释放空间前会调用析构函数完成空间中资源的清理
💦 内存泄漏
1、什么是内存泄漏,以及内存泄漏的危害
什么是内存泄漏:
内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。
具体的说,从我们的几个区域角度看,除了堆之外其它区域的内存是不需要我们管的 —— 栈、数据段、代码段;所以更具体的说内存泄漏就是在堆上申请了空间,我们不用这块空间了,且它没有释放,就存在内存泄漏,因为你不用了,也没有还给系统,别人也用不了。通俗点说:内存泄漏的本质就是站着茅坑不拉屎。
内存泄漏的危害:
长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现内存泄漏会导致响应越来越慢,最终卡死。
int main()
{
char* p = new char[1024 * 1024 * 1024];//1个G
return 0;
}
📝说明
当我们调试起来就可以打开任务管理器看到我们的程序:
上面的程序存在内存泄漏,一次泄漏 1G,但是多次泄漏,对我们的系统也没有什么影响 ❓
一个程序正常结束后,会把映射的内存都会释放掉, 所以上面的程序,我们虽然没有主动释放,但是进程结束也会释放掉,对于进程和线程相关的知识,会在 Linux 内系统的学习。
那内存泄漏好像也没啥事,因为进程正常结束都会释放,其实不然,如下场景:
- 少数情况:进程没有正常结束 ——僵尸进程,就可能存在资源没释放
- 多数情况:长期运行的服务器程序,比如王者荣耀的后台服务 (只有升级时才会停,且都是半夜),服务器每次一运行就是两三个月,如果每天内存泄漏一点,可能上线才一个月,服务器就越来越慢了
- 其它情况:物联网设备,如扫地机器人、冰箱等,它们的内存很小,它们就更经不起内存泄漏的折腾了,所以它们在设计的时候是绝不允许内存泄漏的
正常停机升级这是正常的流程,意外挂掉在公司叫事故,比如你去了一家公司,你把公司的服务器搞崩了,那么可能你们组的年终奖都无了,甚至严重的还会被开了。
对 C++ 而言,我们需要主动释放内存;对 JAVA 而言,我们不需要主动释放内存,因为 JAVA 后台有垃圾回收器,接管了内存释放,当然接管也会付出一些代价,而 C++ 是一个极度关注性能的语言。
2、内存泄漏的分类(了解)
C/C++ 程序中一般我们关心两种方面的内存泄漏:
- 堆内存泄漏 (Heap leak) 堆内存指的是程序执行中依据须要分配通过 malloc / calloc / realloc / new 等从堆中分配的一块内存,用完后必须通过调用相应的 free 或者 delete 删掉。假设程序的设计错误导致这部分内存没有被释放,那么以后这部分空间将无法再被使用,就会产生 Heap Leak。
- 系统资源泄漏 指程序使用系统分配的资源,比方套接字、文件描述符、管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。
3、如何检测内存泄漏(了解)
如何检测内存泄漏:
- 在 linux下内存泄漏检测:Linux 下几款内存泄漏检测工具
- 在 windows 下使用第三方工具:VLD 工具说明
- 其它工具:内存泄漏工具比较
4、如何避免内存泄漏
如何避免内存泄漏:
- 智能指针 (将会在 C++ 进阶进行学习)
- 内存泄漏检测工具 (如上)
内存泄漏非常常见,解决方案分为两种:
- 事前预防型。如智能指针等
- 事后查错型。如泄漏检测工具
💦 如何一次在堆上申请4G的内存
32 位程序在虚拟进程地址空间只有 4G,还有 1G 是供内核使用的,况且还有其它的,怎么能申请 4G;但如果是 64 位程序,可以认为这个虚拟进程空间我们是用不完的,因为它是 2^64^ 个字节。
因为我的机器是 64 位的,当然可以支持 32 位、64 位,所以我们这里切换成 64 位即可:
int main()
{
try
{
//char* p = new char[0x7fffffff];//2G
char* p = new char[0xffffffff];//4G
printf("%p\n", p);
}
catch (const exception& e)
{
cout << "内存申请失败" << endl;
}
return 0;
}